当锁定文件时,为什么会出现OverlappingFileLockException异常?

5

我尝试使用以下代码锁定文件并向其写入:

public class TrainSetBuildTask implements Runnable {
    private String pathname;
    public TrainSetBuildTask(String pathname){
        this.pathname = pathname;
    }

    @Override
    public void run() {
          try {
              String content = "content\n";
             //write to a file
             FileOutputStream os = new FileOutputStream(new File(pathname), true);
             FileChannel channel = os.getChannel();
             FileLock lock = channel.tryLock();
             if (lock != null) {
                ByteBuffer bytes = ByteBuffer.wrap(content.getBytes()); 
                channel.write(bytes);
                lock.release();
             }
             channel.close();
             os.close();
          } catch (IOException e) {
              e.printStackTrace();
          }
     }
 }

同时创建两个类实例的新线程:

    String pathname = "/home/sjtu123/test.arff";
    TrainSetBuildTask task1 = new TrainSetBuildTask(pathname);
    Thread t1 = new Thread(task1);
    TrainSetBuildTask task2 = new TrainSetBuildTask(pathname);
    Thread t2 = new Thread(task2);
    t1.start();
    t2.start();

我遇到了OverlappingFileLockException错误。我想知道为什么会出现这种情况,因为我只在每个线程中锁定文件一次?如何修复我的代码?

2个回答

4
你不能同时锁定同一个文件超过一次。你需要使用Java锁对象来确保只有一个线程尝试锁定文件,或以其他方式协调线程等待,直到没有其他线程在锁定。
来自手册
文件锁是代表整个Java虚拟机持有的。它们不适用于控制同一虚拟机内多个线程对文件的访问。

1
那么 FileLock 的用途是什么呢?既然它不能用于协调想要写入文件的线程。@Attila - jerry_sjtu
1
这是为了防止其他程序在持有锁的同时访问文件。异常表示其他人(在本例中是您自己的线程)已经锁定了该文件。如果您不想放弃(从程序的角度来看),您可以稍后再尝试,例如希望到那时锁已被释放。 - Attila
我在编写上述代码之前已经阅读了手册,但不理解它们的含义。现在我明白了,谢谢。@Attila - jerry_sjtu

2

文件锁定是全局的,而不仅仅是在线程或进程之间。

你需要像这样按顺序运行两个线程:

String pathname = "/home/sjtu123/test.arff";
TrainSetBuildTask task1 = new TrainSetBuildTask(pathname);
Thread t1 = new Thread(task1);
TrainSetBuildTask task2 = new TrainSetBuildTask(pathname);
Thread t2 = new Thread(task2);
t1.start();
t1.join(); // Wait for t1 to die.
t2.start();

评论后的新版本:

FileWriter pathname = new FileWriter("/home/sjtu123/test.arff");
TrainSetBuildTask task1 = new TrainSetBuildTask(pathname);
Thread t1 = new Thread(task1);
TrainSetBuildTask task2 = new TrainSetBuildTask(pathname);
Thread t2 = new Thread(task2);
t1.start();
t2.start();
t1.join();
j2.join();
synchronized (pathname){
    pathname.close();
}

and

public class TrainSetBuildTask implements Runnable {
    private FileWriter pathname;
    public TrainSetBuildTask(FileWriter pathname){
        this.pathname = pathname;
    }

    @Override
    public void run() {
          try {
             // Do work
             synchronized (pathname){
             // Write to file
             }
          } catch (IOException e) {
              e.printStackTrace();
          }
     }
 }

我希望t2除了写入文件之外还能做一些其他的工作。但是你的代码意味着线程t2只有在线程t1完成工作后才能开始工作,无论t2是否想要写入被t1锁定的文件。@Johannes - jerry_sjtu
这在问题中并不是完全明显的,但是请看看新版本。它可能不是100%正确,但你应该能够得到大致的想法。 - Johannes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接