【问题标题】:How can I lock a file using java (if possible)如何使用 java 锁定文件(如果可能)
【发布时间】:2010-09-12 18:50:02
【问题描述】:

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

澄清:

我有一个 Java 应用程序,它列出了一个文件夹并打开列表中的每个文件进行处理。它一个接一个地处理每个文件。每个文件的处理包括读取它并根据内容进行一些计算,大约需要 2 分钟。我还有另一个 Java 应用程序,它做同样的事情,但写在文件上。我想要的是能够同时运行这些应用程序,所以场景是这样的。 ReadApp 列出文件夹并找到文件 A、B、C。它打开文件 A 并开始读取。 WriteApp 列出文件夹并找到文件 A、B、C。它打开文件 A,看到它是打开的(通过异常或任何方式)并转到文件 B。ReadApp 完成文件 A 并继续到 B。它看到它已打开并继续到 C。重要的是,WriteApp 在 ReadApp 读取同一文件时不写入,反之亦然。它们是不同的过程。

【问题讨论】:

  • 您是指 process(两个 JVM)还是线程(同一个 JVM)中的“进程”。对答案的影响至关重要。
  • 在此处查看显示解决方案的示例代码:stackoverflow.com/a/58871479/5154619

标签: java file-io


【解决方案1】:

如果您使用 winscp 或 ftp 传输,请将此用于 unix:

public static void isFileReady(File entry) throws Exception {
        long realFileSize = entry.length();
        long currentFileSize = 0;
        do {
            try (FileInputStream fis = new FileInputStream(entry);) {
                currentFileSize = 0;
                while (fis.available() > 0) {
                    byte[] b = new byte[1024];
                    int nResult = fis.read(b);
                    currentFileSize += nResult;
                    if (nResult == -1)
                        break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("currentFileSize=" + currentFileSize + ", realFileSize=" + realFileSize);
        } while (currentFileSize != realFileSize);
    }

【讨论】:

    【解决方案2】:

    FileChannel.lock 可能是你想要的。

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

    (免责声明:代码未经编译,当然也未经测试。)

    注意API doc for FileLock 中标题为“平台依赖项”的部分。

    【讨论】:

    • 更重要的是要明白,锁是针对JVM的,不适合锁文件供单个JVM内的个别线程访问。
    • 你需要一个可写流(即FileOutputStream)。
    • @Javier 你呢?我没试过。 API 文档中没有任何内容表明这是一项要求。 FileOutputStreamReader 没有多大用处。
    • 是的,我试过了,它抛出了NonWritableChannelException,因为lock() 试图获取一个独占锁,但这需要写访问。如果您有一个 input 流,则可以使用 lock(0L, Long.MAX_VALUE, false) 获取共享锁并且只需要读取访问权限。如果您在阅读时想要排他锁,也可以使用以读写模式打开的RandomAccessFile……但这会禁止并发读者。
    • @Javier 我想你的意思是说lock(0L, Long.MAX_VALUE, true),而不是lock(0L, Long.MAX_VALUE, false)。最后一个参数是boolean shareddocs.oracle.com/javase/8/docs/api/java/nio/channels/…
    【解决方案3】:

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

    FileChannel.lock()

    【讨论】:

    • 也许吧。取决于 OP 的“过程”是什么意思。 “文件锁代表整个 Java 虚拟机持有。它们不适用于控制同一虚拟机内的多个线程对文件的访问。”
    • @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时的意思
    • @Harry 他从文档中引用:download.oracle.com/javase/6/docs/api/java/nio/channels/… 这意味着它对线程不可见,但会影响其他进程。
    • @Harry:要为这些 necro-cmets 添加更多内容,假设您正在使用 Java 为带有 Tomcat 的网站提供服务。您可能有很多线程,每个线程都服务于来自网络浏览器的一个请求。但是,它们都控制着相同的文件锁定机制,就像厨房里有太多厨师一样。一个请求可能在第二个请求的中间完成,突然你的文件在你还在做某事的时候被“解锁”了,然后像 cronjob 这样的其他进程可能会锁定它,然后你意外地丢失了你的文件锁定,您的请求无法完成...
    【解决方案4】:

    下面是一个示例 sn-p 代码,用于锁定文件,直到它的进程由 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();
            }
        }
    
    }
    

    【讨论】:

    • Java 11 于 2011 年发布
    【解决方案5】:

    不要使用java.io 包中的类,而是使用java.nio 包。后者有一个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,所以在我可以在新实例上读取它之后!
    • 这个看起来不错,但它不起作用。我每次都会得到 OverlappingFileLockException,即使文件甚至不存在
    • 如果你在锁后调用tryLock会出现问题,如示例中所写
    • 对于锁定,在 finally 中进行锁定释放通常很重要,以确保即使出现异常也能释放它。如果您能解决此问题,那就太好了,这样人们就不会产生错误。好的,接受的答案中的 try with resources 语句通常可能更好。
    【解决方案6】:

    这可能不是您要寻找的,而是为了从另一个角度解决问题......

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

    【讨论】:

    • 两个进程不能使用同步,同一个进程只能有两个线程。
    【解决方案7】:

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

    【讨论】:

    • 你能详细说明一下吗?我的意思是,RandomAccess File 的锁在多大程度上比流锁更好或更安全
    • 链接到下面发布的简单示例
    • Paralife - 抱歉耽搁了 - 刚刚注意到您的问题。来自流的锁将是读锁(用于输入流)和独占的全通道写锁(用于输出流)。我的经验是,来自 RAF 的锁定允许更细粒度的控制(即您可以锁定文件的某些部分)。
    【解决方案8】:

    【讨论】:

      猜你喜欢
      • 2010-12-17
      • 1970-01-01
      • 2011-07-28
      • 2014-11-09
      • 2011-11-26
      • 2021-06-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多