33

My Java program wants to read a file which can be locked by another program writing into it. I need to check if the file is locked and if so wait until it is free. How do I achieve this?

The Java program is running on a Windows 2000 server.

Nathan
  • 8,093
  • 8
  • 50
  • 76
Hellnar
  • 62,315
  • 79
  • 204
  • 279

10 Answers10

38

Should work in Windows:

File file = new File("file.txt");
boolean fileIsNotLocked = file.renameTo(file);
Vlad
  • 736
  • 1
  • 7
  • 11
  • 3
    This is the only answer that worked for me. Minor caveat: it obvoiusly actually writes (renames) the file. For my purposes that's fine, but one should be aware of it. – Michael A. Schaffrath Jul 10 '18 at 15:16
  • The accepted answer did not work for me, but this answer did. Thank you – Louis Apr 01 '19 at 12:31
  • Wonderful. For my purpose, it also works in Linux, since files can be written even if they're open by other processes. – Eric Duminil Jul 19 '19 at 13:30
  • It's probably obvious, but this allows for a race condition between declaring the boolean and then using the information to access or not access the file, *e.g.* checking if the file is in use, in the meantime a different process accesses the file, then you attempt an access and run into trouble. I find the response useful still. – Koenigsberg Apr 07 '22 at 13:36
  • Nevermind, does not work for me at all. I am locking a file with a different process as we speak, then use this to check if the file is used by another process, which returns `false` incorrectly. Java 11, Win11. – Koenigsberg Apr 07 '22 at 14:28
15

Under Windows with Sun's JVM, the FileLocks should work properly, although the JavaDocs leave the reliability rather vague (system dependent).

Nevertheless, if you only have to recognize in your Java program, that some other program is locking the file, you don't have to struggle with FileLocks, but can simply try to write to the file, which will fail if it is locked. You better try this on your actual system, but I see the following behaviour:

File f = new File("some-locked-file.txt");
System.out.println(f.canWrite()); // -> true
new FileOutputStream(f); // -> throws a FileNotFoundException

This is rather odd, but if you don't count platform independence too high and your system shows the same behaviour, you can put this together in a utility function.

With current Java versions, there is unfortunately no way to be informed about file state changes, so if you need to wait until the file can be written, you have to try every now and then to check if the other process has released its lock. I'm not sure, but with Java 7, it might be possible to use the new WatchService to be informed about such changes.

jarnbjo
  • 33,923
  • 7
  • 70
  • 94
  • 7
    This behaviour is incorrect for Windows, because File.canWrite() only checks the MS-DOS read-only flag, not the ACLs for the file. Therefore it will give false positives for files which you don't have access to write to. – Hakanai Oct 17 '13 at 04:33
  • 1
    Thanks Trejkaz! The behaviour of this method on windows is strange. I tried to verify after a watchevent the file is ready and this methods return always true even is the copy of the file is not over. – Nereis Oct 21 '14 at 06:36
  • File locks in Windows are only *advisory*. There is no guarantee that other files cannot read/write a locked file. – The Coordinator Apr 30 '15 at 21:03
  • There are two caveats to canWrite. As @Trejkaz said it just checks the flag, but the JVM may also have special privileges to overwrite this anyway. – Pieter De Bie Dec 14 '18 at 08:19
  • 2
    Note: If it's an existing file, then it removes all the data from the file. – TechJ Apr 29 '19 at 11:18
  • 1
    Use `new FileOutputStream(file, true)` so not to remove the file content (if the file exists). – Dimitar II Aug 01 '19 at 08:13
6

Use a FileLock in all the Java applications using that file and have them run within the same JVM. Otherwise, this can't be done reliably.

Ben S
  • 68,394
  • 30
  • 171
  • 212
4

If multiple processes (which can be a mix of Java and non-Java) might be using the file, use a FileLock. A key to using file locks successfully is to remember that they are only "advisory". The lock is guaranteed to be visible if you check for it, but it won't stop you from doing things to the file if you forget. All processes that access the file should be designed to use the locking protocol.

erickson
  • 265,237
  • 58
  • 395
  • 493
2

The best way is to use FileLock, but in my case (jdk 1.6) I tried with success:

public static boolean isFileUnlocked(File file) {
        try {
            FileInputStream in = new FileInputStream(file);
            if (in!=null) in.close();
            return true;
        } catch (FileNotFoundException e) {
            return false;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return true;
    }
venergiac
  • 7,469
  • 2
  • 48
  • 70
  • 1
    I used FileLock, and I got the same FileNotFoundException when trying to make the channel for FileLock. So even with FileLock, you are going to trip up at the FileNotFoundException (at least with an Excel type lock), therefore I think this is the way to do it. – EngineerWithJava54321 Jul 17 '15 at 12:00
  • not so relevant to the question, but I just wanted to add that the check `if (in != null)` is certainly not necessary, this condition will always be true. – bvdb Apr 04 '17 at 09:57
2

You can try to get an exclusive lock on the file. As long as the exclusive lock cannot be obtained, another program has a lock (exclusive or shared) on the file.

2

I tryed combination of answers (@vlad) on windows with access to Linux Smb share and it worked for me. The first part was enough for lock like Excel but not for some editors. I added second part (rename) for testing both situations.

public static boolean testLockFile(File p_fi) {
    boolean bLocked = false;
    try (RandomAccessFile fis = new RandomAccessFile(p_fi, "rw")) {
      FileLock lck = fis.getChannel().lock();
      lck.release();
    } catch (Exception ex) {
      bLocked = true;
    }
    if (bLocked)
      return bLocked;
    // try further with rename
    String parent = p_fi.getParent();
    String rnd = UUID.randomUUID().toString();
    File newName = new File(parent + "/" + rnd);
    if (p_fi.renameTo(newName)) {
      newName.renameTo(p_fi);
    } else
      bLocked = true;
    return bLocked;
  }
Claudio
  • 69
  • 5
2

For Windows, you can also use:

new RandomAccessFile(file, "rw")

If the file is exclusively locked (by MS Word for example), there will be exception: java.io.FileNotFoundException: <fileName> (The process cannot access the file because it is being used by another process). This way you do not need to open/close streams just for the check.

Note - if the file is not exclusively locked (say opened in Notepad++) there will be no exception.

Dimitar II
  • 2,299
  • 32
  • 33
1

Improved Amjad Abdul-Ghani answer, I found that no error was produced until attempting to read from the file

public static boolean isFilelocked(File file) {
     try {
         try (FileInputStream in = new FileInputStream(file)) {
             in.read();
             return false;
         }
     } catch (FileNotFoundException e) {
         return file.exists();
     } catch (IOException ioe) {
         return true;
     }
 }
Brook
  • 11
  • 1
0

Tested on windows only : you can check if the file is locked as following enhanced venergiac answer: check for (file.exist()) file exists but with FileNotFoundException means is locked! you will notice this message (The process cannot access the file because it is being used by another process)

        public static boolean isFilelocked(File file) {
                try {

                    FileInputStream in = new FileInputStream(file);
                    in.close();
                    return false;
                } catch (FileNotFoundException e) {
                    if(file.exist()){ 
                       return true;
                     }
                     return false;
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return false;
            }