用于读写的Java FileLock

时间:2010-02-15 21:20:23

标签: java file-locking filelock

我有一个进程,它会经常从cron调用,以读取其中包含某些与移动相关的命令的文件。我的进程需要读取和写入此数据文件 - 并将其锁定以防止其他进程在此期间触摸它。用户可以执行完全独立的过程(可能)写入/附加到该相同的数据文件。我希望这两个进程运行良好,并且一次只能访问一个文件。

nio FileLock似乎是我所需要的(没有编写我自己的信号量类型文件),但我无法锁定它进行阅读。我可以锁定并写入正常,但在读取时尝试创建锁定时会出现NonWritableChannelException。甚至可以锁定文件进行阅读吗?看起来像RandomAccessFile更接近我的需要,但我不知道如何实现它。

以下是失败的代码:

FileInputStream fin = new FileInputStream(f);
FileLock fl = fin.getChannel().tryLock();
if(fl != null) 
{
  System.out.println("Locked File");
  BufferedReader in = new BufferedReader(new InputStreamReader(fin));
  System.out.println(in.readLine());
          ...

FileLock行抛出异常。

java.nio.channels.NonWritableChannelException
 at sun.nio.ch.FileChannelImpl.tryLock(Unknown Source)
 at java.nio.channels.FileChannel.tryLock(Unknown Source)
 at Mover.run(Mover.java:74)
 at java.lang.Thread.run(Unknown Source)

看看JavaDocs,它说

  

尝试写入最初未打开以进行写入的通道时,抛出未经检查的异常。

但我不一定要写信给它。当我尝试创建FileOutpuStream等用于写入目的时,我很高兴,直到我尝试在同一个文件上打开FileInputStream。

3 个答案:

答案 0 :(得分:17)

(a)您是否知道锁定文件不会阻止其他进程触及它,除非他们也使用锁? (b)你必须通过可写信道锁定。在“rw”模式下通过RandomAccessFile获取锁定,然后打开FileInputStream。一定要关闭它们!

答案 1 :(得分:13)

如果使用tryLock(0L, Long.MAX_VALUE, true)创建锁定会更好。

这会创建一个共享锁,这是阅读的正确方法。

tryLock()tryLock(0L, Long.MAX_VALUE, false)的简写,即它请求独占写锁。

答案 2 :(得分:0)

我编写了一个测试程序和bash命令来确认文件锁的有效性:

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;

public class FileWriterTest
{
    public static void main(String[] args) throws Exception
    {
        if (args.length != 4)
        {
            System.out.println("Usage: FileWriterTest <filename> <string> <sleep ms> <enable lock>");
            System.exit(1);
        }

        String filename = args[0];
        String data = args[1];
        int sleep = Integer.parseInt(args[2]);
        boolean enableLock = Boolean.parseBoolean(args[3]);

        try (RandomAccessFile raFile = new RandomAccessFile(new File(filename), "rw"))
        {
            FileLock lock = null;
            if (enableLock)
            {
                lock = raFile.getChannel().lock();
            }

            Thread.sleep(sleep);
            raFile.seek(raFile.length());
            System.out.println("writing " + data + " in a new line; current pointer = " + raFile.getFilePointer());
            raFile.write((data+"\n").getBytes());

            if (lock != null)
            {
                lock.release();
            }
        }
    }
}

使用此bash命令运行以检查其是否有效:

for i in {1..1000}
do
java FileWriterTest test.txt $i 10 true &
done

您应该看到写入仅每10毫秒发生一次(来自输出),最后所有数字都出现在文件中。

输出:

/tmp wc -l test.txt
1000 test.txt
/tmp

没有锁的相同测试显示数据丢失:

for i in {1..1000}
do
java FileWriterTest test.txt $i 10 false &
done

输出:

/tmp wc -l test.txt
764 test.txt
/tmp

修改它以测试tryLock应该很容易。