我遇到了这种情况,并不明白为什么会发生这种情况。有人可以帮我理解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次,在第1次等待时开始第2次。由program1锁定的文件被program2覆盖。以下示例:
PROGRAM1:
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();
Program2中:
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();
由于
答案 0 :(得分:1)
仅指定文件锁以对抗其他文件锁。
来自Javadoc:
锁是否实际阻止其他程序访问锁定区域的内容是系统相关的,因此未指定。某些系统的本机文件锁定功能仅仅是建议性的,这意味着程序必须协作地观察已知的锁定协议以保证数据的完整性。在其他系统上,本机文件锁是必需的,这意味着如果一个程序锁定文件的某个区域,则实际上阻止其他程序以违反该锁的方式访问该区域。在其他系统上,可以基于每个文件配置本机文件锁是建议性的还是必需的。为了确保跨平台的一致和正确行为,强烈建议使用此API提供的锁,就像它们是建议锁一样。
答案 1 :(得分:1)
当您获得FileLock
时,您将获得整个JVM。这就是为什么创建更多FileOutputStream
并在同一个JVM中覆盖相同文件的原因永远不会被FileLock
阻止 - JVM拥有锁。因此,OverlappingFileLockException
并不是要告诉您锁定不可用(tryLock
通过返回null
发出信号),这是为了告诉您有一个编程错误:尝试获取您已拥有的锁。
当尝试从不同的JVM访问同一文件时,您偶然发现锁定不一定阻止其他进程写入锁定区域,这只是阻止它们锁定该区域。而且由于您使用的是constructor which truncates existing files,这可能会在您尝试获取锁定之前发生。
一种解决方案是使用new FileOutputStream(fileToWrite, true)
来避免截断文件。无论您是在同一JVM中还是在不同的进程中打开文件,这都可以正常工作。
但是,您可能不希望追加到该文件。我想你想在成功获得锁的情况下覆盖。在这种情况下,FileOutputStream
的构造函数不会帮助您,因为它们会强制您决定是截断还是追加。
解决方案是放弃旧API和open the FileChannel
directly(至少需要Java 7)。然后你有足够的standard open options truncating和appending是不同的。省略两者都允许覆盖而不会急切地截断文件:
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");
}
}
因为它无论如何都需要Java 7,您可以使用自动资源管理进行清理。请注意,此代码使用CREATE
,这意味着创建文件(如果它不存在)已经熟悉的行为,与要求3>}相比{/ 3}} em>该文件不存在。
由于指定的选项,open
操作可能会创建文件但不会截断它。所有后续操作仅在获取锁定成功时执行。