Java 在已锁定的文件上打开文件输出流会覆盖它
我遇到了这种情况,不明白为什么会发生这种情况。有人能帮我理解nio文件锁的行为吗 我使用FileOutputStream打开了一个文件,在使用nio FileLock获得独占锁后,我向文件中写入了一些数据。没有释放锁。在同一文件上打开了另一个FileOutputStream,目的是获取锁并执行写入操作,并预期此操作会失败。但打开第二个FileOutputStream会重写已锁定的文件,该文件在尝试获取第二个锁之前就已写入数据。这是预期的吗?我的理解是获得独占锁将阻止对锁定的文件进行任何更改。在尝试获取另一个锁时,如何防止覆盖我的文件?(好像另一个进程试图在另一个vm上锁定同一个文件?) 我尝试过的示例程序:Java 在已锁定的文件上打开文件输出流会覆盖它,java,concurrency,filelock,Java,Concurrency,Filelock,我遇到了这种情况,不明白为什么会发生这种情况。有人能帮我理解nio文件锁的行为吗 我使用FileOutputStream打开了一个文件,在使用nio FileLock获得独占锁后,我向文件中写入了一些数据。没有释放锁。在同一文件上打开了另一个FileOutputStream,目的是获取锁并执行写入操作,并预期此操作会失败。但打开第二个FileOutputStream会重写已锁定的文件,该文件在尝试获取第二个锁之前就已写入数据。这是预期的吗?我的理解是获得独占锁将阻止对锁定的文件进行任何更改。在尝
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进行覆盖,即使在锁定可以防止覆盖的系统上也是如此。而且它会针对不同进程的其他锁进行锁定。