【问题标题】:Why does OverlappingFileLockException happen when locking a file?为什么锁定文件时会发生 OverlappingFileLockException?
【发布时间】:2012-03-28 11:03:11
【问题描述】:

我尝试锁定一个文件并使用以下代码对其进行写入:

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。我想知道为什么会发生这种情况,因为我只是在每个线程中锁定文件一次?如何修复我的代码?

【问题讨论】:

    标签: java multithreading locking


    【解决方案1】:

    您不能同时锁定同一个文件多次。您要么需要使用 java 锁定对象来确保一次只有一个线程尝试锁定文件,要么协调线程等待直到没有其他线程锁定。

    来自manual

    代表整个 Java 虚拟机持有文件锁。它们不适用于控制同一虚拟机内的多个线程对文件的访问。

    【讨论】:

    • 那么FileLock不能用来协调要写文件的线程有什么用呢? @Attila
    • 是为了防止其他人(例如其他程序)与持有锁的人同时访问该文件。该异常表明其他人(在这种情况下是您自己的线程之一)已经锁定了该文件。如果您不想放弃(从您的程序的角度来看),您可以稍后再试,例如希望到那时锁会被释放。
    • 我在写上面这些代码之前已经阅读了手册,但不明白它们的含义。现在我明白了,谢谢。 @Attila
    【解决方案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();
    

    cmets之后的新版本:

    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();
    }
    

    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锁定的文件。 @约翰内斯
    • 这在问题中并不完全明显,但请看一下新版本。它可能不是 100% 正确,但您应该了解总体思路。
    猜你喜欢
    • 1970-01-01
    • 2023-04-09
    • 2019-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多