如何使用Java锁定文件(如果可能)


121

我有一个使用FileReader打开文件的Java进程。如何防止另一个(Java)进程打开该文件,或者至少通知第二个进程该文件已打开?如果文件打开(这解决了我的问题),这是否使第二个进程自动获得异常?还是我必须在第一个进程中使用某种标记或参数来显式打开它?

澄清:

我有一个Java应用程序,其中列出了一个文件夹并打开列表中的每个文件进行处理。它依次处理每个文件。每个文件的处理包括读取文件并根据内容进行一些计算,大约需要2分钟。我也有另一个Java应用程序,它执行相同的操作,但写在文件上。我想要的是能够同时运行这些应用程序,这样情况就可以这样。ReadApp列出文件夹并查找文件A,B,C。它将打开文件A并开始读取。WriteApp列出该文件夹并查找文件A,B,C。它将打开文件A,看到已打开(通过异常或其他方式),然后转到文件B。ReadApp完成了文件A,然后继续到B。打开并继续到C。WriteApp不能 在ReadApp读取同一文件时进行写入,反之亦然。它们是不同的过程。


12
你的意思是“过程”中的过程(二的JVM)或线程(同一个JVM)。对答案的影响至关重要。
Stu Thompson

检查此处显示解决方案的示例代码:stackoverflow.com/a/58871479/5154619
Davi Cavalcanti,

Answers:


117

FileChannel.lock可能就是您想要的。

try (
    FileInputStream in = new FileInputStream(file);
    java.nio.channels.FileLock lock = in.getChannel().lock();
    Reader reader = new InputStreamReader(in, charset)
) {
    ...
}

(免责声明:代码未编译,并且肯定未经过测试。)

请注意FileLockAPI文档中标题为“平台依赖项”的部分。


22
更重要的是,了解JVM的锁,而不适合锁定文件以供单个JVM中的各个线程访问。
Stu Thompson

11
您需要可写流(即FileOutputStream)。
哈维尔

@哈维尔,你呢?我没试过 没有任何要求从API文档中跳出来。FileOutputStream对于Reader。来说并没有多大用处。
Tom Hawtin-抢险活动

18
是的,我尝试过它并抛出NonWritableChannelException,因为lock()尝试获取排他锁,但这需要写访问权限。如果有输入流,则可以使用lock(0L, Long.MAX_VALUE, false)它来获取共享锁,并且只需要读取访问权限。RandomAccessFile如果您想在读取时使用排他锁,也可以在读写模式下使用打开方式,但这会禁止并发读取器。
哈维尔2013年

6
@哈维尔我想你的意思lock(0L, Long.MAX_VALUE, true)不是lock(0L, Long.MAX_VALUE, false)。最后一个参数有boolean shared docs.oracle.com/javase/8/docs/api/java/nio/channels/...
约翰·沙利文

60

不要使用java.io包中的类,而要使用java.niopackage。后者有一FileLock类。您可以将锁应用于FileChannel

 try {
        // Get a file channel for the file
        File file = new File("filename");
        FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

        // Use the file channel to create a lock on the file.
        // This method blocks until it can retrieve the lock.
        FileLock lock = channel.lock();

        /*
           use channel.lock OR channel.tryLock();
        */

        // Try acquiring the lock without blocking. This method returns
        // null or throws an exception if the file is already locked.
        try {
            lock = channel.tryLock();
        } catch (OverlappingFileLockException e) {
            // File is already locked in this thread or virtual machine
        }

        // Release the lock - if it is not null!
        if( lock != null ) {
            lock.release();
        }

        // Close the file
        channel.close();
    } catch (Exception e) {
    }

顺便说一句,我正在将此技巧stackoverflow.com/a/35885/1422630上的当前pid写入锁定文件,因此可以在新实例上读取它了!
Aquarius Power

1
这个看起来不错,但是没有用。我每次都会收到OverlappingFileLockException,即使文件根本不存在也是如此
Gavriel

1
如果你打电话的tryLock锁之后,因为它是写例如问题会发生
伊戈尔·武科维奇

17

如果您可以使用Java NIOJDK 1.4或更高版本),那么我认为您正在寻找java.nio.channels.FileChannel.lock()

FileChannel.lock()


5
也许。取决于OP的“过程”的含义。“文件锁代表整个Java虚拟机持有。它们不适合控制同一虚拟机中的多个线程对文件的访问。”
Stu Thompson

@Stu:我知道您很久以前已经回答了这个问题,但是我希望您能阐明您说的意思File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine
Thang Pham

3
@Harry He从文档中引用:download.oracle.com/javase/6/docs/api/java/nio/channels / ...这意味着它对线程不可见,但会影响其他进程。
Artur Czajka,2011年

@Harry:要在这些坏注释中添加更多内容,请想象您正在使用Java通过Tomcat为网站提供服务。您可能有很多线程,每个线程都处理来自Web浏览器的一个请求。但是,它们都控制相同的文件锁定机制,就像厨房中的太多厨师一样。一个请求可能在第二个请求的中间完成,突然间,当您还在某个文件中间的时候,您的文件被“解锁”,然后诸如cronjob之类的其他进程可能将其锁定,然后您意外丢失了自己的文件锁,您的请求无法完成...
Darien


5

这可能不是您想要的,但是为了从另一个角度解决问题……。

这两个Java进程可能想要访问同一应用程序中的同一文件吗?也许您可以仅通过一个同步方法(或者甚至更好的是使用JSR-166)过滤对文件的所有访问?这样,您可以控制对文件的访问,甚至可以控制队列访问请求。


3
两个进程不能使用同步,同一进程中只能有两个线程。
罗恩侯爵

3

使用RandomAccessFile,获取它的频道,然后调用lock()。输入或输出流提供的通道没有足够的特权来正确锁定。确保在finally块中调用unlock()(关闭文件并不一定会释放锁)。


你能详细说明吗?我的意思是,从
某种程度上

链接到下面发布的简单示例
Touko,2009年

生命宝(Paralife)-抱歉,延迟-刚注意到您的问题。来自流的锁将是读锁(对于输入流)和排他的全通道写锁(对于输出流)。我的经验是,RAF的锁定允许进行更精细的控制(即,您可以锁定文件的某些部分)。
凯文·戴

1

下面是一个示例代码片段,用于锁定文件,直到由JVM完成处理为止。

 public static void main(String[] args) throws InterruptedException {
    File file = new File(FILE_FULL_PATH_NAME);
    RandomAccessFile in = null;
    try {
        in = new RandomAccessFile(file, "rw");
        FileLock lock = in.getChannel().lock();
        try {

            while (in.read() != -1) {
                System.out.println(in.readLine());
            }
        } finally {
            lock.release();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.