Java Unix 中的移动操作是原子的吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18706419/
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
Is a move operation in Unix atomic?
提问by Chaos
Suppose there are 2 processes P1 and P2, and they access a shared file Foo.txt
.
假设有 2 个进程 P1 和 P2,并且它们访问共享文件Foo.txt
。
Suppose P2 is reading from Foo.txt
. I don't want P1 to write to Foo.txt
while P2 is reading it.
假设 P2 正在读取Foo.txt
。我不希望 P1Foo.txt
在 P2 读取时写入。
So I thought I could make P1 write to Foo.tmp
and as a last step, rename Foo.tmp
to Foo.txt
. My programming language is Java
所以我想我可以让 P1 写入Foo.tmp
并作为最后一步,重命名Foo.tmp
为Foo.txt
. 我的编程语言是 Java
So my question is, would this ensure that P2 reads the correct data from Foo.txt
? Would the rename operation be committed once P2 completes reading the file?
所以我的问题是,这会确保 P2 从中读取正确的数据Foo.txt
吗?一旦 P2 完成读取文件,是否会提交重命名操作?
EDIT
编辑
I tried to recreate this scenario as follows:
我尝试按如下方式重新创建此场景:
My P1 code is something like this:
我的 P1 代码是这样的:
File tempFile = new File(path1);
File realFile = new File(path2);
BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
for(int i=0;i<10000;i++)
writer.write("Hello World\n");
writer.flush();
writer.close();
tempFile.renameTo(realFile);
and my P2 code is :
我的 P2 代码是:
BufferedReader br = new BufferedReader(new FileReader(file));
String line = null;
while(true) {
while((line=br.readLine())!=null){
System.out.println(line);
Thread.sleep(1000);
}
br.close();
}
My Sample shared File:
我的示例共享文件:
Test Input
Test Input
Test Input
I'm starting P1 and P2 almost simulataneously (P2 starting first).
我几乎同时启动 P1 和 P2(P2 先启动)。
So according to my understanding, even though P1 has written a new Foo.txt, since P2 is already reading it, it should read the old Foo.txt content until it re-opens a BufferedReader to Foo.txt.
所以根据我的理解,即使P1写了一个新的Foo.txt,由于P2已经在读了,它应该会读到旧的Foo.txt内容,直到它重新打开一个BufferedReader到Foo.txt。
But what actually happens is P2 reads Test Input
thrice, as is expected from the input, but after that it reads the new content which was written by P1.
但实际发生的是 P2 读取Test Input
三次,正如从输入中预期的那样,但之后它读取 P1 写入的新内容。
Output from P2:
P2 的输出:
Test Input
Test Input
Test Input
Hello World
Hello World
Hello World
.
.
.
So it doesn't work as it should. Am I testing this scenario wrong? I feel like there's something I'm missing out.
所以它不能正常工作。我测试这个场景错了吗?我觉得我错过了一些东西。
回答by Claudiu
- move(rename) is atomic if done on the same device. (device = same disk/partition)
- If
Foo.txt
exits moveFoo.tmp
toFoo.txt
most likely will fail. (But if you first deleteFoo.txt
and then move, it should work). What happens is that a file is not physically deleted until all file handlers are closed (there is no process that uses that file). Also, after remainingFoo.tmp
toFoo.txt
you will have 2 Foo.txt files. One that is deleted but still opened in memory (basically that file does not have a reference on disk anymore) and one that actually resides on disk. - But, after move, in second process you need to reopen the file.
- 如果在同一设备上完成,移动(重命名)是原子的。(设备 = 相同的磁盘/分区)
- 如果
Foo.txt
退出移动Foo.tmp
到Foo.txt
很可能会失败。(但如果你先删除Foo.txt
然后移动,它应该可以工作)。发生的情况是在关闭所有文件处理程序之前不会物理删除文件(没有使用该文件的进程)。此外,在剩下Foo.tmp
给Foo.txt
你之后,你将有 2 个 Foo.txt 文件。一个被删除但仍然在内存中打开(基本上该文件在磁盘上不再有引用)和一个实际驻留在磁盘上。 - 但是,移动后,在第二个过程中,您需要重新打开文件。
Let me know if we are on the same page with #1.
让我知道我们是否与#1 处于同一页面。
回答by Kent
why not use FileChannel.lock
?
为什么不使用FileChannel.lock
?
here is an example:
这是一个例子:
http://examples.javacodegeeks.com/core-java/nio/filelock/create-shared-file-lock-on-file/
http://examples.javacodegeeks.com/core-java/nio/filelock/create-shared-file-lock-on-file/
回答by Chris Dodd
A UNIX rename
operation is atomic (see rename(2)). The UNIX mv
command uses rename if the source and target path are on the same physical device. If the target path is on a different device, the rename will fail, and mv
will copy the file (which is not atomic).
UNIXrename
操作是原子操作(请参阅 rename(2))。mv
如果源路径和目标路径在同一物理设备上,则UNIX命令使用重命名。如果目标路径在不同的设备上,重命名将失败,mv
并将复制文件(这不是原子的)。
If the target file path exists, the rename
will atomically remove it from the file system and replace it with the new file. The file won't actually be deleted until its reference count drops to zero, so if another process is currently reading the file, it will keep reading the old file. Once all processes have closed the old file, its reference count will drop to zero and the file storage space will be reclaimed.
如果目标文件路径存在,rename
则将自动将其从文件系统中删除并替换为新文件。该文件在其引用计数降至零之前不会被实际删除,因此如果另一个进程当前正在读取该文件,它将继续读取旧文件。一旦所有进程都关闭了旧文件,它的引用计数将降为零,文件存储空间将被回收。