javaniotruncatefile-locking

java nio FileLock.release() throws ClosedChannelException and truncates my file


I've got e:\myfile.txt with some random contents:

abcabcxyz...abc

Then I try to use nio FileLock like below:

import java.io.*;
import java.nio.channels.*;
public class FileLockTest{
    public static void main(String[] args){
        FileLock lock = null;
        try(FileChannel ch=new FileOutputStream(args[0]).getChannel())
        {
            lock=ch.lock();//No param exclusive lock.
            //lock(0L,ch.size(),true) for shared lock.
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(lock!=null){
                try{
                    lock.release();//throws exception!
                }catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

Upon running it and I got:

PS> java FileLockTest e:\myfile.txt
java.nio.channels.ClosedChannelException
        at sun.nio.ch.FileLockImpl.release(Unknown Source)
        at FileLockTest.main(FileLockTest.java:17)

I open myfile.txt again, all contents are missing, seems FileLock has truncated it, really strange to me.

Could you help to explain what goes wrong here?


Solution

  • Your content is missing because you've opened your file with an output stream in write mode instead of append. When opening a file in write mode, the content previously written is erased from the file. While in append mode, the content is kept and any new write is added at the end.

    In your case, since you're using a FileOutputStream, you need to instantiate ch with the two-parameter constructor and pass true to append. Otherwise, the stream will always be constructed in write mode.

    FileChannel ch = new FileOutputStream(args[0], true)
    

    Also, as already said in the comments, what is giving you the ClosedChannelException is the fact that FileLock is bound to a file channel that has already been closed. In fact, once you reach the finally block, the channel is no longer open, and any attempt to release a lock on a closed channel simply fails.

    So, to answer your question, it's not the lock which erases your data, but rather the way how you access the file. Besides, since both FileOutputStream and FileLock implement the AutoClosable interface, you could include both of them in the try-with statement and rewrite your code like this:

    public static void main(String[] args) {
        try (FileChannel ch = new FileOutputStream(args[0], true).getChannel();
             FileLock lock = ch.lock()) {
    
            //do your operations....
    
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }