Java文件锁定

13
我有几个线程(其中一些是由进程X生成的,另一些是由进程Y生成的,等等),每个线程都需要写入一个名为MyFile的文件。但是,如果Thread T1首先开始写入MyFile,那么当Thread T2开始写入时,它需要等待T1释放该文件,以便它可以读取在Thread T1中编写的内容。换句话说,每个线程都将具有finalizeThread方法,如下所示:
private void finalizeThread() {
    File f = new File("MyFile.dat");
    f.createNewFile();  // atomically creates the file, if it doesn't exist
    locked_section {
        readContentsFromFile(f); // read contents if some other thread already modified the file
        modifyContentsFromFile(f); // modify
        writeFile(f); // write, so that new threads can see the content modified by this thread
    }
}

我的问题是:如何完成上面代码中的locked_section?我查阅了FileLock类,但是Javadoc中说:"文件锁定是代表整个Java虚拟机持有的。它们不适用于在同一虚拟机内由多个线程控制对文件的访问。"


你确定你真的想使用finalize()方法吗? - Dmitry
不,该方法的名字实际上不是 finalize。它是一个在线程完成其工作后调用的方法/回调,因为我没有使用标准的 Java 并发 API 线程。我将重命名它,以避免与 Object#finalize 引起混淆。 - João Silva
3个回答

15
如果文件只在您的程序中访问,那么同步锁对象就可以了。但是如果您想在您处理文件时防止其他程序更改该文件,您可以使用Java的文件锁定特性,在java.nio.channels.FileLock中实现(示例)。正如文本所说,在某些操作系统上,如果程序没有检查现有的文件锁定,则仍然可以更改文件。

感谢您的反馈。是的,它只能被我的程序访问,并且每个线程在继续之前都会/可以检查锁的存在。问题在于我正在使用的API可以从几个不同的进程中生成线程,有些线程来自同一进程内部,我担心可能会出现与此相关的问题,因为FileLock javadoc提到了这一点。 - João Silva
也许我误解了什么,但是你说的不同进程生成的线程是什么意思?一个进程有自己的地址空间,它创建的任何线程都共享该地址空间。你是指多个进程各自生成多个线程,这些线程可能尝试写入同一个文件吗? - D.C.
2
Javadoc表示:“文件锁是代表整个Java虚拟机持有的。它们不适合控制在同一虚拟机内由多个线程访问文件的情况。文件锁对象可安全地供多个并发线程使用。”最后两个句子不是相互排斥的吗?我认为有人得查看源代码...接下来的段落中说:“这个文件锁定API旨在直接映射到底层操作系统的本地锁定设施。”因此,它应该像flock(Linux)或LockFileEx(Windows)一样工作。 - AndiDog
1
不,这是有道理的。关于这两个句子,第一个句子的意思是“不要使用文件锁来控制线程之间对文件的访问”,而第二个句子则表示“多个线程可以使用相同的文件锁,只要它们知道它们都拥有相同的文件锁”。 - danben
@danben:好的,谢谢你的限定,现在有意义了 :) - AndiDog
根据您的使用情况,使用FileChannel.tryLock可能比使用FileChannel.lock更好,因为tryLock不会阻塞。如果您使用FileChannel.lock,该方法将等待锁被另一个线程(或进程)解除,然后才会继续执行。在Oracle Java 7上的测试表明,即使它声明抛出FileLockInterruptionException,它也是一个不可中断的操作。 - Christopher Schultz

2

与其共享锁,也许你可以拥有一个独立的进程来负责维护文件锁。为了开始读/修改/写步骤,线程必须通过HTTP或消息传递等方式向中央进程请求锁。如果请求被拒绝,线程会进入睡眠状态,然后再次尝试。否则,线程将读取/修改/写入,然后通知锁定进程它正在释放锁。


1
如果你要使用一个中央进程,最好让这个进程自己处理读/写操作,这样你就不必担心当线程在获得锁之后死掉时会发生什么情况(尽管还是需要考虑,但是会比较简单)。 - wds

0

你需要在某个对象上进行同步。例如:

synchronized(fileLockObject) {
    readContentsFromFile(f);
    modifyContentsFromFile(f);
    writeFile(f);
}

2
你好 Anon.,感谢你的评论。然而,我无法这样做,因为这些线程是由不同的进程生成的,我不能在所有线程之间共享该对象,因此需要另一种方法。这些也不是标准的Java concurrency.*线程,而是由Oracle API创建的线程。 - João Silva
如果它们是不同的进程(因此是分离的虚拟机),您可以使用FileLock类。 - Anon.
是的,但有些是由同一进程生成的。但是,也许混合策略会起作用。谢谢。 - João Silva
7
@Stephen: 文档上并不是这么写的。 - Anon.
@StephenC,它会如何实现? - Pacerier

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