Dón*_*nal 13 java file-io locking
我一直试图用来FileLock获得对文件的独占访问权限,以便:
因为在Windows上(至少),您似乎无法删除,重命名或写入已在使用的文件.我写的代码看起来像这样:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
public abstract class LockedFileOperation {
public void execute(File file) throws IOException {
if (!file.exists()) {
throw new FileNotFoundException(file.getAbsolutePath());
}
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
try {
// Get an exclusive lock on the whole file
FileLock lock = channel.lock();
try {
doWithLockedFile(file);
} finally {
lock.release();
}
} finally {
channel.close();
}
}
public abstract void doWithLockedFile(File file) throws IOException;
}
Run Code Online (Sandbox Code Playgroud)
以下是一些证明问题的单元测试.您需要在类路径上安装Apache commons-io来运行第3次测试.
import java.io.File;
import java.io.IOException;
import junit.framework.TestCase;
public class LockedFileOperationTest extends TestCase {
private File testFile;
@Override
protected void setUp() throws Exception {
String tmpDir = System.getProperty("java.io.tmpdir");
testFile = new File(tmpDir, "test.tmp");
if (!testFile.exists() && !testFile.createNewFile()) {
throw new IOException("Failed to create test file: " + testFile);
}
}
public void testRename() throws IOException {
new LockedFileOperation() {
@Override
public void doWithLockedFile(File file) throws IOException {
if (!file.renameTo(new File("C:/Temp/foo"))) {
fail();
}
}
}.execute(testFile);
}
public void testDelete() throws IOException {
new LockedFileOperation() {
@Override
public void doWithLockedFile(File file) throws IOException {
if (!file.delete()) {
fail();
}
}
}.execute(testFile);
}
public void testWrite() throws IOException {
new LockedFileOperation() {
@Override
public void doWithLockedFile(File file) throws IOException {
org.apache.commons.io.FileUtils.writeStringToFile(file, "file content");
}
}.execute(testFile);
}
}
Run Code Online (Sandbox Code Playgroud)
没有一个测试通过.前2个失败,最后一个抛出此异常:
java.io.IOException: The process cannot access the file because another process has locked a portion of the file
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:247)
at org.apache.commons.io.IOUtils.write(IOUtils.java:784)
at org.apache.commons.io.IOUtils.write(IOUtils.java:808)
at org.apache.commons.io.FileUtils.writeStringToFile(FileUtils.java:1251)
at org.apache.commons.io.FileUtils.writeStringToFile(FileUtils.java:1265)
Run Code Online (Sandbox Code Playgroud)
似乎该lock()方法锁定文件,然后阻止我重命名/删除/写入它.我的假设是锁定文件会让我对该文件进行独占访问,因此我可以重命名/删除/写入它,而不必担心其他任何进程是否也在访问它.
要么我误解,FileLock要么它不适合解决我的问题.
Kev*_*ock 16
有关另一个进程的消息仅表示系统上的某个进程已打开该文件.它实际上并不检查该进程是否与尝试删除/重命名文件的进程相同.在这种情况下,同一程序打开了文件.你打开它来获得锁定.这里的锁几乎没有价值,特别是如果你这样做是为了删除或重命名操作.
要执行您想要的操作,您需要锁定目录条目.这在Java中不可用,可能在Windows中不可用.这些(删除和插入)操作是原子操作.这意味着操作系统负责为您锁定目录和其他文件系统结构.如果另一个进程(或您自己的进程)打开了该文件,则这些操作将失败.如果您尝试以独占方式锁定文件(目录条目)而另一个进程(或您自己的)将文件打开,则锁定将失败.没有区别,但尝试执行锁定只会使操作变得复杂,并且在这种情况下,使操作无法进行(即,在尝试执行操作之前始终打开文件).
现在写入文件是一个有效的锁定操作.锁定要写入的文件或文件的一部分,然后它将起作用.在Windows上,此锁定机制是必需的,因此另一个打开/文件描述符将无法写入锁定下的任何部分.
编辑
根据JavaDoc FileChannel.lock,它与调用相同FileChannel.lock(0L, Long.MAXVALUE, false).这是从第一个字节到最后一个字节的区域的独占锁定.
其次,根据JavaDoc FileLock
锁实际上是否阻止另一个程序访问锁定区域的内容是系统相关的,因此未指定.某些系统的本机文件锁定功能仅仅是建议性的,这意味着程序必须协作地观察已知的锁定协议以保证数据的完整性.在其他系统上,本机文件锁是必需的,这意味着如果一个程序锁定文件的某个区域,则实际上阻止其他程序以违反该锁的方式访问该区域.在其他系统上,可以基于每个文件配置本机文件锁是建议性的还是必需的.为了确保跨平台的一致和正确行为,强烈建议使用此API提供的锁,就像它们是建议锁一样.
编辑
对于testWrite方法.公共I/O静态方法上的JavaDoc是稀疏的,但是说"如果文件不存在,则将字符串写入创建文件的文件......"并且由于此方法需要File而不是打开的流,它可能会打开文件内部.可能它没有打开具有共享访问权限的文件,也没有打开附加访问权限.这意味着现有的open和lock(你打开以获取锁定的通道)阻止了这种使用.要了解更多信息,您需要获取该方法的源代码并查看它正在做什么.
编辑
对不起,我有所纠正.我检查了Windows API,Windows上必须进行文件锁定.这就是写入失败的原因.第一个打开(你的new RandomAccessFile)和锁定文件被锁定.打开写入字符串成功但写入失败,因为另一个打开(文件描述符)具有强制独占锁定下的文件的完整范围 - 也就是说,在释放锁定之前,没有其他文件描述符可以写入文件.
请注意,锁定与文件描述符NOT进程或线程相关联.