1

I want to test to see if I can lock file X. If the file doesn't exist or can't be locked, fail. It sounds simple, but I keep running into dead-ends. A short-cut to the answer would be to provide a way to get a FileChannel I can make an exclusive lock on, without any risk of creating a file. Let me explain...

I can't use NIO lock() without a writable FileChannel and I can't get a writable FileChannel without opening file X in such a way that if it doesn't exist, it is created. Every Java method I've found for trying to open file X as writable creates it if it doesn't exist and there doesn't seem to be a way to exclusively lock a file on a read-only FileChannel.

Even checking to confirm the file exists first is problematic. Here's my latest code:

private LockStatus openAndLockFile(File file) {

            if (Files.notExists(file.toPath())) {
                synchronized (fileList) {
                    removeFile(file);
                }
                return LockStatus.FILE_NOT_FOUND;
            }
            try {   
                rwFile = new RandomAccessFile(file, "rw");
                fileLock = rwFile.getChannel().lock();                  
            } 
            catch ...

The problem with this code is that the file might exist when the NotExists code runs, but be gone by the time the new RandomAccessFile(file, "rw") runs. This is because I'm running this code in multiple threads and multiple processes which means the code has to be air-tight and rock solid.

Here's an example of the problem with my existing code running in two processes:

1: Process A detects the new file

2: Process B detects the same file

3: Process A processes the file and moves it to another folder

Problem ---> Process B Accidentally creates an empty file. OOPS!!!

4: Process B detects the new file created by process B and processes it creating a duplicate file which is 0 bytes.

5: Process A also detects the new file accidentally created by process B and tries to process it...

Bigger screenshot

https://i.stack.imgur.com/cHlsJ.png

Here's an example using C# of what I'm trying to do:

Stream iStream = File.Open("c:\\software\\code.txt", FileMode.Open,
    FileAccess.Read, FileShare.None)

Any help or hints are greatly appreciated! Thanks!

Richard Brightwell
  • 3,012
  • 2
  • 20
  • 22
  • Synchronise around the critical blocks – MadProgrammer May 31 '15 at 05:38
  • Thanks for the suggestion. How does that work when this code runs in completely separate processes? possibly on different machines? – Richard Brightwell May 31 '15 at 05:41
  • Then obviously using a file is the wrong approach, unless the file systems are shared between those machines. This might be better: http://stackoverflow.com/questions/1059580/distributed-lock-service – Erik Pragt May 31 '15 at 06:17
  • Thanks for the suggestion, but I'm not using files to make a distributed lock system. I'm processing these files. The file lock is to ensure that only one thread is processing any given file at any point in time. – Richard Brightwell May 31 '15 at 06:24
  • I am looking at exactly the same problm. Did you find a solution? Thanks – Shashi Oct 03 '20 at 10:39

1 Answers1

0

If you are trying to prevent two threads in the same application (same JVM) from processing the same file, then you should be implementing this using regular Java locks, not file locks. File locks are granted to the JVM and are reentrant ... so if one thread "locks" a file, another thread can acquire a lock on the same file.

What I would do is to create a thread-safe locking class that wraps a HashSet<File>, where the File objects denote absolute file paths for files that exist. Then implement "file locking" by locking on the File objects.


Unfortunately not only will they be in different JVMs, they will sometimes be on different server.

In that case, the best strategy is probably to use a database to implement the locks.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216