How can I lock a file using java (if possible)
Asked Answered
N

8

134

I have a Java process that opens a file using a FileReader. How can I prevent another (Java) process from opening this file, or at least notify that second process that the file is already opened? Does this automatically make the second process get an exception if the file is open (which solves my problem) or do I have to explicitly open it in the first process with some sort of flag or argument?

To clarify:

I have a Java app that lists a folder and opens each file in the listing for processing it. It processes each file after the other. The processing of each file consists of reading it and doing some calculations based on the contents and it takes about 2 minutes. I also have another Java app that does the same thing but instead writes on the file. What I want is to be able to run these apps at the same time so the scenario goes like this. ReadApp lists the folder and finds files A, B, C. It opens file A and starts the reading. WriteApp lists the folder and finds files A, B, C. It opens file A, sees that is is open (by an exception or whatever way) and goes to file B. ReadApp finishes file A and continues to B. It sees that it is open and continues to C. It is crucial that WriteApp doesn't write while ReadApp is reading the same file or vice versa. They are different processes.

Nonce answered 24/9, 2008 at 15:59 Comment(2)
Do you mean 'process' as in process (two JVMs) or thread (same JVM). The impact on the answer is paramount.Toothache
check sample code showing solution here: https://mcmap.net/q/82752/-java-filelock-for-reading-and-writingSexy
E
124

FileChannel.lock is probably what you want.

try (
    FileInputStream in = new FileInputStream(file);
    java.nio.channels.FileLock lock = in.getChannel().lock();
    Reader reader = new InputStreamReader(in, charset)
) {
    ...
}

(Disclaimer: Code not compiled and certainly not tested.)

Note the section entitled "platform dependencies" in the API doc for FileLock.

Emancipator answered 24/9, 2008 at 16:20 Comment(6)
More importantly, understand that the lock of for the JVM, and not suitable for locking the file for access by individual threads within a single JVM.Toothache
You need a writable stream (i.e. FileOutputStream).Heliogabalus
@Heliogabalus Do you? I've not tried. Nothing jumps out of the API docs saying that is a requirement. FileOutputStream isn't going to be much use for a Reader.Emancipator
Yes, I tried it and it throws NonWritableChannelException, because lock() attempts to acquire an exclusive lock, but that requires write access. If you have an input stream, you can use lock(0L, Long.MAX_VALUE, false) which acquires a shared lock and only requires a read access. You can also use a RandomAccessFile opened in read-write mode if you want a exclusive lock while reading... but that would forbid concurrent readers.Heliogabalus
@StuThompson, on Windows this actually locks out other threads as well. On Linux this does not lock unless other process/thread is using explicit locking as well.Selfassertion
@Heliogabalus I think you mean to say lock(0L, Long.MAX_VALUE, true), not lock(0L, Long.MAX_VALUE, false). the last argument there is boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/…Palaver
D
65

Don't use the classes in thejava.io package, instead use the java.nio package . The latter has a FileLock class. You can apply a lock to a FileChannel.

 try {
        // Get a file channel for the file
        File file = new File("filename");
        FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

        // Use the file channel to create a lock on the file.
        // This method blocks until it can retrieve the lock.
        FileLock lock = channel.lock();

        /*
           use channel.lock OR channel.tryLock();
        */

        // Try acquiring the lock without blocking. This method returns
        // null or throws an exception if the file is already locked.
        try {
            lock = channel.tryLock();
        } catch (OverlappingFileLockException e) {
            // File is already locked in this thread or virtual machine
        }

        // Release the lock - if it is not null!
        if( lock != null ) {
            lock.release();
        }

        // Close the file
        channel.close();
    } catch (Exception e) {
    }
Downwind answered 26/1, 2012 at 15:20 Comment(4)
btw, I am writing on the lock file the current pid from this tip https://mcmap.net/q/80796/-how-can-a-java-program-get-its-own-process-id, so after I can read it on the new instance!Meredi
this one looked good, but it doesn't work. I get the OverlappingFileLockException every time, even when the file didn't even exsistLexicology
Problem will happen if You call tryLock after lock as it is written in exampleSabian
For locking it's generally crucial to do the lock release in a finally to make sure it's released even if there is an exception. It'd be nice if you could fix this, so that people don't produce bugs. OK, the try with resources statement in the accepted answer is probably usually even better.Nicolenicolea
P
19

If you can use Java NIO (JDK 1.4 or greater), then I think you're looking for java.nio.channels.FileChannel.lock()

FileChannel.lock()

Pyrene answered 24/9, 2008 at 16:13 Comment(4)
Maybe. Depends on what OP meant by 'process'. "File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine."Toothache
@Stu: I know you have answered this question long time ago, but I hope you can elaborate what you mean when you said File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machineSingletary
@Harry He's citing from the docs: download.oracle.com/javase/6/docs/api/java/nio/channels/… It means it's invisible to threads but affects other processes.Deuteranope
@Harry: To add even more to these necro-comments, imagine you are using Java for serving websites with Tomcat. You might have a lot of threads, each serving one request from a web-browser. However, they all control the same file locking mechanism like too many cooks in a kitchen. One request might finish in the middle of a second one, and suddenly your file got "unlocked" while you were still in the middle of something, and then some other process like a cronjob might lock it, and then you've unexpectedly lost your lock and your request cannot finish...Caesium
M
8

use java.nio.channels.FileLock in conjunction with java.nio.channels.FileChannel

Marshall answered 24/9, 2008 at 16:14 Comment(0)
H
5

This may not be what you are looking for, but in the interest of coming at a problem from another angle....

Are these two Java processes that might want to access the same file in the same application? Perhaps you can just filter all access to the file through a single, synchronized method (or, even better, using JSR-166)? That way, you can control access to the file, and perhaps even queue access requests.

Henpeck answered 25/9, 2008 at 3:48 Comment(1)
Two processes can't use synchronisation, only two threads in the same process.Dracula
V
3

Use a RandomAccessFile, get it's channel, then call lock(). The channel provided by input or output streams does not have sufficient privileges to lock properly. Be sure to call unlock() in the finally block (closing the file doesn't necessarily release the lock).

Voyeurism answered 25/9, 2008 at 3:40 Comment(3)
can you elaborate? I mean, to what extend is the lock by RandomAccess File better or safer than streams oneNonce
Link to simple example posted belowEpiscopalian
Paralife - sorry for the delay - just noticed your question. The locks from streams are going to be read locks (for input streams) and exclusive, full channel write locks (for output streams). My experience has been that the locks from RAF allow for more fine grained control (i.e. you can lock portions of a file).Voyeurism
T
1

Below is a sample snippet code to lock a file until it's process is done by JVM.

 public static void main(String[] args) throws InterruptedException {
    File file = new File(FILE_FULL_PATH_NAME);
    RandomAccessFile in = null;
    try {
        in = new RandomAccessFile(file, "rw");
        FileLock lock = in.getChannel().lock();
        try {

            while (in.read() != -1) {
                System.out.println(in.readLine());
            }
        } finally {
            lock.release();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
Typography answered 24/5, 2017 at 3:57 Comment(1)
Java 11 was released 2011Rola
D
1

Use this for unix if you are transferring using winscp or ftp:

public static void isFileReady(File entry) throws Exception {
        long realFileSize = entry.length();
        long currentFileSize = 0;
        do {
            try (FileInputStream fis = new FileInputStream(entry);) {
                currentFileSize = 0;
                while (fis.available() > 0) {
                    byte[] b = new byte[1024];
                    int nResult = fis.read(b);
                    currentFileSize += nResult;
                    if (nResult == -1)
                        break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("currentFileSize=" + currentFileSize + ", realFileSize=" + realFileSize);
        } while (currentFileSize != realFileSize);
    }
Dehydrogenate answered 12/8, 2020 at 15:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.