Java 在已锁定的文件上打开文件输出流会覆盖它

Java 在已锁定的文件上打开文件输出流会覆盖它,java,concurrency,filelock,Java,Concurrency,Filelock,我遇到了这种情况,不明白为什么会发生这种情况。有人能帮我理解nio文件锁的行为吗 我使用FileOutputStream打开了一个文件,在使用nio FileLock获得独占锁后,我向文件中写入了一些数据。没有释放锁。在同一文件上打开了另一个FileOutputStream,目的是获取锁并执行写入操作,并预期此操作会失败。但打开第二个FileOutputStream会重写已锁定的文件,该文件在尝试获取第二个锁之前就已写入数据。这是预期的吗?我的理解是获得独占锁将阻止对锁定的文件进行任何更改。在尝

我遇到了这种情况,不明白为什么会发生这种情况。有人能帮我理解nio文件锁的行为吗

我使用FileOutputStream打开了一个文件,在使用nio FileLock获得独占锁后,我向文件中写入了一些数据。没有释放锁。在同一文件上打开了另一个FileOutputStream,目的是获取锁并执行写入操作,并预期此操作会失败。但打开第二个FileOutputStream会重写已锁定的文件,该文件在尝试获取第二个锁之前就已写入数据。这是预期的吗?我的理解是获得独占锁将阻止对锁定的文件进行任何更改。在尝试获取另一个锁时,如何防止覆盖我的文件?(好像另一个进程试图在另一个vm上锁定同一个文件?)

我尝试过的示例程序:

        File fileToWrite = new File("C:\\temp\\myfile.txt");
        FileOutputStream fos1 = new FileOutputStream(fileToWrite);
        FileOutputStream fos2 =null;
        FileLock lock1,lock2 =null;
        lock1=fos1.getChannel().tryLock();
        if(lock1!=null){
            //wrote date to myfile.txt after acquiring lock
            fos1.write(data.getBytes());
            //opened myfile.txt again and this replaced the file
            fos2 = new FileOutputStream(fileToWrite);
            //got an overlappingfilelock exception here
            lock2=fos2.getChannel().tryLock();
            fos2.write(newdata.getBytes());
            }

        lock1.release();
        fos1.close();
        if(lock2!=null)
            lock2.release();
        fos2.close();
还尝试将上述内容拆分为两个程序。第一次执行,第二次启动,第1次等待。被program1锁定的文件被program2覆盖。样本如下:

方案1:

    File fileToWrite = new File("C:\\temp\\myfile.txt");
    FileOutputStream fos1 = new FileOutputStream(fileToWrite);
    FileLock lock1 =null;
    lock1=fos1.getChannel().tryLock();
    if(lock1!=null){
        //wrote date to myfile.txt after acquiring lock
        fos1.write(data.getBytes());
        System.out.println("wrote data and waiting");
        //start other program while sleep
        Thread.sleep(10000);
        System.out.println("finished wait");
        }

    lock1.release();
    fos1.close();
方案2:

   File fileToWrite = new File("C:\\temp\\myfile.txt");
    System.out.println("opening 2nd out stream");
    //this overwrote the file
    FileOutputStream fos2 = new FileOutputStream(fileToWrite);
    FileLock lock2 =null;
    lock2=fos2.getChannel().tryLock();
    //lock is null here
    System.out.println("lock2="+lock2);
    if(lock2!=null){
        //wrote date to myfile.txt after acquiring lock
        System.out.println("writing  NEW data");
        fos2.write(newdata.getBytes());
        }

    if(lock2!=null)
        lock2.release();
    fos2.close();

谢谢

仅文件锁仅指定用于对抗其他文件锁

从:

锁是否实际阻止另一个程序访问锁定区域的内容取决于系统,因此未指定。某些系统的本机文件锁定功能只是建议性的,这意味着程序必须协同遵守已知的锁定协议,以保证数据完整性。在其他系统上,本机文件锁是强制性的,这意味着如果一个程序锁定文件的某个区域,那么其他程序实际上会被阻止以违反该锁定的方式访问该区域。在其他系统上,本地文件锁是建议性的还是强制性的,都可以根据每个文件进行配置。为了确保跨平台的行为一致且正确,强烈建议将此API提供的锁当作建议锁使用


当您获取
文件锁时,您将为整个JVM获取它。这就是为什么在同一JVM中创建更多的
FileOutputStream
并覆盖同一文件永远不会被
FileLock
阻止的原因-JVM拥有锁。因此,
OverlappingFileLockException
并不是要告诉您锁不可用(这将由
tryLock
通过返回
null
发出信号),而是要告诉您存在编程错误:试图获取您已经拥有的锁

当试图从不同的JVM访问同一个文件时,您会发现,锁定并不一定会阻止其他进程写入锁定区域,它只是阻止它们锁定该区域。由于您使用的是,这可能发生在您尝试获取锁之前

一种解决方案是避免截断文件。无论您是在同一JVM中打开文件还是在不同的进程中打开文件,这都是有效的

但是,可能您不想附加到文件中。我想如果你成功获得了锁,你会想要覆盖。在这种情况下,
FileOutputStream
的构造函数不能帮助您,因为它们迫使您决定是截断还是追加

解决方案是放弃旧的API和(至少需要Java7)。那么你就有了很多不同的地方。省略这两个选项可以在不急于截断文件的情况下进行覆盖:

try(FileChannel fch=FileChannel.open(fileToWrite.toPath(),
                                     StandardOpenOption.CREATE, StandardOpenOption.WRITE)){
  try(FileLock lock=fch.tryLock()) {
    if(lock!=null) {
      // you can directly write into the channel
      // but in case you really need an OutputStream:
      OutputStream fos=Channels.newOutputStream(fch);
      fos.write(testData.getBytes());
      // you may explicitly truncate the file to the actually written content:
      fch.truncate(fch.position());
      System.out.println("waiting while holding lock...");
      LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
    }
    else System.out.println("couldn't acquire lock");
  }
}
因为它需要Java7,所以您可以使用自动资源管理进行清理。注意,这段代码使用了一种已经很熟悉的行为,即如果文件不存在,则创建该文件,而这需要文件不存在


由于指定的选项,
open
操作可能会创建文件,但不会截断文件。所有后续操作仅在成功获取锁时执行。

但在锁定期间,不允许删除文件。Get exception—“进程无法访问该文件,因为它正被另一个进程使用。”这是因为您打开了它,而不是因为锁。这只是温杜的事。让你想想吧。它要么锁定所有其他用途,如覆盖(第1段),要么锁定其他锁定(第2段)。不能同时使用这两种方法。@EJP:它可能会锁定以防止被其他进程覆盖,但正如您所引用的“依赖于系统,因此未指定”。我要说的是,它肯定不会阻止同一JVM进行覆盖,即使在锁定可以防止覆盖的系统上也是如此。而且它会针对不同进程的其他锁进行锁定。