Check If a File Is Locked in Java

Check if a file is locked in Java

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.

What is the best way to check if a directory and its content is locked?

Checking each file in the directory strikes me as slow and prone to race conditions. In your example you're getting a list of files in the directory at an instant and then you test each; meanwhile the creator thread may still be adding files. Even if every file in your list is accessible, the creator may not be finished writing the directory.

It's somewhat old-school and not very fancy but I would use either a temporary directory name while creation is in progress or write a lockfile as the first file and delete it when the creator is finished.

In the first case:

String baseDirectory = "{whatever}";
String workDirectory = "workDirectory" + counter;
Path tempPath = FileSystems.getDefault().getPath(baseDirectory, ".temp_" + workDirectory);
Path workPath = FileSystems.getDefault().getPath(baseDirectory, workDirectory);
Files.createDirectory(tempPath);
// Write the contents of the directory.
// [...]
Files.move(tempPath, workPath, CopyOptions.ATOMIC_MOVE);

In the second case:

String baseDirectory = "{whatever}";
String workDirectory = "workDirectory" + counter;
Path workPath = FileSystems.getDefault().getPath(baseDirectory, workDirectory);
Files.createDirectory(workPath);
Path lockFile = workPath.resolve("LOCKFILE");
Files.createFile(lockFile);
// Write the contents of the directory.
// [...]
Files.delete(lockFile);

The idea in both cases is that the creator task explicitly signals when it's done creating the directory. If you just wait for a pause in operations, it could just be waiting for a large buffer to load over the network.

File Locking in java

The documentation on FileLock class says:

This file-locking API is intended to map directly to the native locking facility of the underlying operating system. Thus the locks held on a file should be visible to all programs that have access to the file, regardless of the language in which those programs are written.

So it seems that you could just use same code as in your first snippet:

File folder = new File("E:\\folder_to_LIST_OF_FILES");
File[] listOfFiles = folder.listFiles();

for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
FilesDto returnDto = new FilesDto();
returnDto.setFileName(FilenameUtils.removeExtension(listOfFiles[i].getName()));

File file = new File("filename");
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
FileLock lock;
try {
lock = channel.tryLock();
} catch (OverlappingFileLockException e) {
// File is already locked
}

if (lock==null) {
returnDto.setStatus("unlocked");
returnDto.setFilePath(listOfFiles[i].getAbsolutePath());

} else {
lock.release();
returnDto.setStatus("Locked");
}

returnDtoList.add(returnDto);
}
}

Java: How to hold off read/writing a file while it's locked

The standard way to use FileLock, is to open the file, e.g. via FileChannel.open, followed by tryLock. The presence of a lock does not prevent other processes from opening the file.

This can be demonstrated by the following program:

import java.io.IOException;
import java.nio.channels.*;
import java.nio.file.*;

class Locking {
public static void main(String[] args) throws IOException, InterruptedException {
if(args.length > 0) {
String me = String.format("%6s ", ProcessHandle.current());
Path p = Paths.get(args[0]);
try(FileChannel fc = FileChannel.open(p,
StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {

FileLock l = fc.tryLock();
if(l == null) System.out.println(me + "could not acquire lock");
else {
System.out.println(me + "got lock");
Thread.sleep(3000);
System.out.println(me + "releasing lock");
l.release();
}
}
}
else {
Path p = Files.createTempFile("lock", "test");
String[] command = {
Paths.get(System.getProperty("java.home"), "bin", "java").toString(),
"-cp", System.getProperty("java.class.path"),
"Locking", p.toString()
};
ProcessBuilder b = new ProcessBuilder(command).inheritIO();
Process p1 = b.start(), p2 = b.start(), p3 = b.start();
p1.waitFor();
p2.waitFor();
p3.waitFor();
Files.delete(p);
}
}
}

which prints something alike

 12116 got lock
13948 could not acquire lock
13384 could not acquire lock
12116 releasing lock

which can be demonstrated online on tio.run

While this program works the same under Windows, this operating system supports opening files unshared, preventing other processes from opening. If a different process has opened the file in that way, we can’t even open it to probe the locking state.

This is not the way, Java opens the file, however, there’s a non-standard open option to replicate the behavior, com.sun.nio.file.ExtendedOpenOption.NOSHARE_WRITE. In recent JDKs, it’s in the jdk.unsupported module.

When we run the following extended test program under Windows

import java.io.IOException;
import java.nio.channels.*;
import java.nio.file.*;
import java.util.HashSet;
import java.util.Set;

class LockingWindows {
public static void main(String[] args) throws IOException, InterruptedException {
if(args.length > 0) {
String me = String.format("%6s ", ProcessHandle.current());
Path p = Paths.get(args[0]);
Set<OpenOption> options
= Set.of(StandardOpenOption.WRITE, StandardOpenOption.APPEND);
if(Boolean.parseBoolean(args[1])) options = addExclusive(options);
try(FileChannel fc = FileChannel.open(p, options)) {
FileLock l = fc.tryLock();
if(l == null) System.out.println(me + "could not acquire lock");
else {
System.out.println(me + "got lock");
Thread.sleep(3000);
System.out.println(me + "releasing lock");
l.release();
}
}
}
else {
Path p = Files.createTempFile("lock", "test");
String[] command = {
Paths.get(System.getProperty("java.home"), "bin", "java").toString(),
"-cp", System.getProperty("java.class.path"),
"LockingWindows", p.toString(), "false"
};
ProcessBuilder b = new ProcessBuilder(command).inheritIO();
for(int run = 0; run < 2; run++) {
Process p1 = b.start(), p2 = b.start(), p3 = b.start();
p1.waitFor();
p2.waitFor();
p3.waitFor();
if(run == 0) {
command[command.length - 1] = "true";
b.command(command);
System.out.println("\nNow with exclusive mode");
}
}
Files.delete(p);
}
}

private static Set<OpenOption> addExclusive(Set<OpenOption> options) {
OpenOption o;
try {
o = (OpenOption) Class.forName("com.sun.nio.file.ExtendedOpenOption")
.getField("NOSHARE_WRITE").get(null);
options = new HashSet<>(options);
options.add(o);
} catch(ReflectiveOperationException | ClassCastException ex) {
System.err.println("opening exclusive not supported");
}
return options;
}
}

we will get something like

  2356 got lock
6412 could not acquire lock
9824 could not acquire lock
2356 releasing lock

Now with exclusive mode
9160 got lock
Exception in thread "main" java.nio.file.FileSystemException: C:\Users\...\Temp\lock13936982436235244405test: The process cannot access the file because it is being used by another process
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
at java.base/sun.nio.fs.WindowsFileSystemProvider.newFileChannel(WindowsFileSystemProvider.java:121)
at java.base/java.nio.channels.FileChannel.open(FileChannel.java:298)
at LockingWindows.main(LockingWindows.java:148)
Exception in thread "main" java.nio.file.FileSystemException: C:\Users\...\Temp\lock13936982436235244405test: The process cannot access the file because it is being used by another process
at java.base/sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:92)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:103)
at java.base/sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:108)
at java.base/sun.nio.fs.WindowsFileSystemProvider.newFileChannel(WindowsFileSystemProvider.java:121)
at java.base/java.nio.channels.FileChannel.open(FileChannel.java:298)
at LockingWindows.main(LockingWindows.java:148)
9160 releasing lock

The similarity to the outcome of your test suggests that the Windows program you ran concurrently to your Java program did use such a mode.

For your Java programs, no such issue should arise, as long as you don’t use that mode. Only when you have to interact with another Windows program not using the collaborative locking, you have to deal with this.

In Java, Is there a way to read a file when that file is locked by other thread?

You could try asking for a shared lock using the three argument version of tryLock.

Here is the appropriate javadoc:
http://download.oracle.com/javase/1.4.2/docs/api/java/nio/channels/FileChannel.html#tryLock%28long,%20long,%20boolean%29

Basically instead of doing
lock=channel.tryLock()
you would do lock = channel.trylock(0, Long.MAX_VALUE, true)

As an aside, you should be careful with file locking in java. While you can guarantee the locks behave as expected within the JVM you can't necessarily be sure that they will behave as expected accross multiple processes.



Related Topics



Leave a reply



Submit