Java FileLock 是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4025721/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does FileLock work?
提问by Dónal
I've been trying to use FileLock
to get exclusive access to a file in order to:
我一直在尝试使用FileLock
来获得对文件的独占访问权限,以便:
- delete it
- rename it
- write to it
- 删除它
- 重命名
- 写信给它
Because on Windows (at least) it seems that you cannot delete, rename, or write to a file that is already in use. The code I've written looks something like this:
因为在 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;
}
Here are some unit tests that demonstrate the problem. You'll need to have Apache commons-io on your classpath to run the 3rd test.
下面是一些演示问题的单元测试。您需要在类路径上安装 Apache commons-io 才能运行第三个测试。
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);
}
}
None of the tests pass. The first 2 fail, and the last throws this exception:
没有一个测试通过。前 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)
It seems like the lock()
method places a lock on the file which then prevents me from renaming/deleting/writing it. My assumption was that locking the file would give me exclusive access to the file, so I could then rename/delete/write it without worrying about whether any other process is also accessing it.
似乎该lock()
方法锁定了文件,然后阻止我重命名/删除/写入它。我的假设是锁定文件将使我能够独占访问该文件,因此我可以重命名/删除/写入它,而不必担心是否有任何其他进程也在访问它。
Either I'm misunderstanding FileLock
or it's not an appropriate solution for my problem.
要么我误解了,FileLock
要么它不是我的问题的合适解决方案。
回答by Eugene Kuleshov
The lock you've for is locking a region inside a file, but not the file itself, so while region is locked you can't delete or rename the file.
您所使用的锁是锁定文件内的区域,而不是文件本身,因此当区域被锁定时,您无法删除或重命名文件。
You may want to look at the Commons Transactionproject.
您可能想查看Commons Transaction项目。
回答by dogbane
The delete
and rename
operations are performed by the operating system and are atomic (on most operating systems), so no locking is required.
的delete
和rename
操作由操作系统执行,并且是原子的(在大多数操作系统上),所以不需要锁定。
To write a string to file, it would be simpler to write to a temporary file first (e.g. foo.tmp) and then rename it once it is ready.
要将字符串写入文件,首先写入临时文件(例如 foo.tmp)然后在准备好后重命名它会更简单。
回答by user207421
Java file locks are specified only to protect against other locks, and nothing else. How they behave on specific platforms, i.e. any extra semantics, is platform-specific.
指定 Java 文件锁只是为了防止其他锁,没有别的。它们在特定平台上的行为方式,即任何额外的语义,是特定于平台的。
回答by Kevin Brock
The message about another process just means that some process on your system has the file open. It does not actually check that that process happens to be the same as the one attempting to delete/rename the file. In this case, the same program has the file opened. You have opened it to get the lock. The lock here has little to no value, especially if you are doing this for delete or rename operations.
有关另一个进程的消息仅表示您系统上的某个进程打开了该文件。它实际上并不检查该过程是否与尝试删除/重命名文件的过程相同。在这种情况下,同一个程序打开了该文件。您已打开它以获取锁。此处的锁几乎没有价值,尤其是在执行删除或重命名操作时。
To do what you want, you would need to lock the directory entry. This is not available in Java and may not be available in Windows. These (delete and insert) operations are atomic. That means that the operating system takes care of locking the directory and other file system structures for you. If another process (or your own) has the file open then these operations will fail. If you are trying to lock the file exclusively (directory entry) and another process (or your own) has the file open, then the lock will fail. There is no difference, but attempting to do the lock just complicates, and in this case, makes the operation impossible (that is, the files are always opened before you attempt to do the operation).
要执行您想要的操作,您需要锁定目录条目。这在 Java 中不可用,也可能在 Windows 中不可用。这些(删除和插入)操作是原子的。这意味着操作系统会为您锁定目录和其他文件系统结构。如果另一个进程(或您自己的进程)打开了该文件,则这些操作将失败。如果您试图以独占方式锁定文件(目录条目)并且另一个进程(或您自己的进程)打开了该文件,则锁定将失败。没有区别,但是尝试锁定只会使操作变得复杂,在这种情况下,操作将无法进行(也就是说,文件总是在您尝试进行操作之前打开)。
Now writing to the file is a valid lock operation. Lock the file or portion of the file that you want to write to and then it will work. On Windows, this lock mechanism is mandatory so another open/file descriptor will not be able to write to any portion that is under the lock.
现在写入文件是一个有效的锁定操作。锁定要写入的文件或文件部分,然后它将起作用。在 Windows 上,此锁定机制是强制性的,因此另一个打开/文件描述符将无法写入锁定下的任何部分。
EDIT
编辑
According to the JavaDoc on FileChannel.lock
, it is the same as calling FileChannel.lock(0L, Long.MAXVALUE, false)
. This is an exclusive lock on a region from the first byte to the last.
根据上的 JavaDoc FileChannel.lock
,它与调用FileChannel.lock(0L, Long.MAXVALUE, false)
. 这是对从第一个字节到最后一个字节的区域的排他锁。
Second, according to JavaDoc on FileLock
二、根据JavaDoc上 FileLock
Whether or not a lock actually prevents another program from accessing the content of the locked region is system-dependent and therefore unspecified. The native file-locking facilities of some systems are merely advisory, meaning that programs must cooperatively observe a known locking protocol in order to guarantee data integrity. On other systems native file locks are mandatory, meaning that if one program locks a region of a file then other programs are actually prevented from accessing that region in a way that would violate the lock. On yet other systems, whether native file locks are advisory or mandatory is configurable on a per-file basis. To ensure consistent and correct behavior across platforms, it is strongly recommended that the locks provided by this API be used as if they were advisory locks.
锁是否真正阻止另一个程序访问锁定区域的内容取决于系统,因此未指定。某些系统的本地文件锁定设施仅仅是建议性的,这意味着程序必须协同遵守已知的锁定协议以保证数据完整性。在其他系统上,本地文件锁是强制性的,这意味着如果一个程序锁定了文件的某个区域,那么实际上会阻止其他程序以违反锁的方式访问该区域。在其他系统上,本机文件锁是建议性的还是强制性的,可以在每个文件的基础上进行配置。为了确保跨平台的行为一致和正确,强烈建议将此 API 提供的锁用作建议锁。
EDIT
编辑
For the testWrite
method. The JavaDoc on the commons I/O static method is sparse but says "Writes a String to a file creating the file if it does not exist.." and being as this method takes a File
instead of an opened stream, it likely opens the file internally. Probably it is not opening the file with shared access and also opening for append access. This means that the existing open and lock (your open to get the channel from which to get the lock) are blocking that use. To understand even more, you would need to get the source for that method and look at what it is doing.
对于testWrite
方法。公共 I/O 静态方法上的 JavaDoc 是稀疏的,但说“如果文件不存在,则将字符串写入创建文件的文件......”并且由于此方法采用一个File
而不是打开的流,因此它可能会打开文件内部。可能它没有以共享访问权限打开文件,也没有打开追加访问权限。这意味着现有的打开和锁定(您打开以获取获取锁定的通道)正在阻止该使用。要了解更多信息,您需要获取该方法的源代码并查看它在做什么。
EDIT
编辑
Sorry, I stand corrected. I checked the Windows API and file locking is mandatory on Windows. This is why the write fails. The first open (your new RandomAccessFile
) and lock has the file locked. The open to write the string succeeds but the write fails because another open (file descriptor) has the full extent of the file under mandatory exclusive lock - that is, no other file descriptor can write to the file until the lock is released.
对不起,我纠正了。我检查了 Windows API,在 Windows 上文件锁定是强制性的。这就是写入失败的原因。第一次打开(您的new RandomAccessFile
)和锁定已锁定文件。写入字符串的打开成功但写入失败,因为另一个打开(文件描述符)具有强制排他锁下的文件的完整范围 - 也就是说,在释放锁之前,没有其他文件描述符可以写入文件。
Note that locking is associated with the file descriptor NOTprocess or thread.
请注意,锁定与文件描述符NOT进程或线程相关联。
回答by itro
You should release the file with method release() before doing any action like rename or delete or ....
在执行重命名或删除或...等任何操作之前,您应该使用方法 release() 释放文件。