Friday, 26 January 2018

How to lock a File before writing in Java?

A file is one of the oldest ways to store data and share data but if you are working in a shared file i.e a file which can be read or write by multiple readers and writers, you need to make sure that the file is locked before you try to write on it. This is needed to ensure that someone doesn't overwrite the data you are writing. Fortunately, Java provides a mechanism to lock a file before writing using the FileLock interface. You can get the handle of FileLock by using FileChannel for writing to a file. The FileChannel class is generally used to write faster in the large file and one of the common way to write binary data in Java.

In order to lock the file before writing, you need to call the tryLock() method on FileChannel. This method attempts to acquire an exclusive lock on this channel's file. It returns a lock object representing the newly-acquired lock, or null if the lock could not be acquired because another program holds an overlapping lock.

This is a non-blocking method and an invocation always returns immediately, either having acquired a lock on the requested region or having failed to do so. If it fails to acquire a lock because an overlapping lock is held by another program then it returns null.

If it fails to acquire a lock for any other reason then an appropriate exception is thrown.

For example, It will throw the OverlappingFileLockException - If a lock that overlaps the requested region is already held by this Java virtual machine, or if another thread is already blocked in this method and is attempting to lock an overlapping region and ClosedChannelException if this channel is closed.

You need to ensure that those exceptions are handled properly and if you are not sure why and how to handle exceptions in Java, I suggest you go through the Error and Exception section of Complete Java 9 Masterclass course by Udemy.

Java Program to lock a file before writing


Now that we understand that we can use FileChannel's tryLock() method and FileLock interface to lock a file before writing, let's see the program in action. This will help you to understand the concept better.

import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.concurrent.TimeUnit;

/*
 * Java Program to lock a file before writing into it.
 */

public class Demo {

  public static void main(String[] args) throws Exception {
 
    RandomAccessFile file = new RandomAccessFile("accounts.txt", "rw");
    FileChannel channel = file.getChannel();
 
    FileLock lock = null;
    try {
      lock = channel.tryLock();
    } catch (final OverlappingFileLockException e) {
      file.close();
      channel.close();
    }

    file.writeChars("writing after lock");
    TimeUnit.HOURS.sleep(1);
    lock.release();
 
    file.close();
    channel.close();

  }

}

In this program, we have created a RandomAccessFile called "accounts.txt" and then retrieved the FileChannel by calling the getChannel() method on it. In order to lock the file before writing into it, we have called the tryLock() method which will acquire the lock.

After acquiring the lock we write some characters into the file and before releasing the lock by calling lock.release() we made our program to sleep for 1 hour using TimeUnit.sleep() method. You can also use the Thread.sleep() method here but I prefer TimeUnit because its explicitly on how much time thread is going to wait.

This part is important to demonstrate what will happen if another program is writing into the file at the same time.

When you first run this program, it will create an accounts.txt file in your project directory and when you open the file you can see the content but the program will not finish it will keep running because of the sleep we have put there.

Then we try to run the program again and at this time, you will see the following error:

Exception in thread "main" java.io.IOException: The process cannot access the file because another process has locked a portion of the file
at java.io.RandomAccessFile.writeBytes(Native Method)
at java.io.RandomAccessFile.writeChars(RandomAccessFile.java:1123)
at Demo.main(Demo.java:26)

This happens because the earlier program hasn't released the lock yet. Remember it was paused before calling the lock.release() method.

This becomes more clear with the following screenshot from Eclipse IDE, where you can see that the earlier program is still running (instance 1) which locked the file and hence when you run the program again, it died with above error related to file locking:

Oracle Java Tutorials and Materials, Oracle Java Guides, Oracle Java Certifications

That's all about how to lock a file before writing in Java. If you are not the exclusive writer on the file or you are working with a shared file then you should always lock the file before writing data into it. Failing to do may result in file corruption and data loss.

You should also make sure to release the lock once you are done with your writing into the file and provide appropriate exception handling to catch OverlappingFileLockException and IOException.

Related Posts