Unix中的移动操作是原子的吗?

时间:2013-09-09 17:22:36

标签: java unix file-locking

假设有2个进程P1和P2,并且它们访问共享文件Foo.txt

假设P2正在从Foo.txt读取。当P2正在阅读时,我不希望P1写入Foo.txt

所以我想我可以让P1写入Foo.tmp,最后一步,将Foo.tmp重命名为Foo.txt。我的编程语言是Java

所以我的问题是,这会确保P2从Foo.txt读取正确的数据吗? P2完成读取文件后,是否会提交重命名操作?

修改

我尝试按如下方式重新创建此方案:

我的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);

我的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();
}

我的示例共享文件:

Test Input
Test Input
Test Input   

我几乎同时开始P1和P2(P2首先开始)。

所以根据我的理解,尽管P1已经写了一个新的Foo.txt,因为P2已经在读它,它应该读取旧的Foo.txt内容,直到它重新打开BufferedReader到Foo.txt。

但实际发生的事情是P2读取Test Input三次,如输入所预期的那样,但之后它会读取由P1写入的新内容。

P2的输出:

Test Input
Test Input
Test Input 
Hello World
Hello World
Hello World
 .
 .
 .

所以它不能正常工作。我测试这种情况错了吗?我觉得我错过了一些东西。

3 个答案:

答案 0 :(得分:31)

UNIX rename操作是原子操作(请参阅rename(2))。如果源路径和目标路径位于同一物理设备上,则UNIX mv命令将使用重命名。如果目标路径位于其他设备上,则重命名将失败,mv将复制该文件(非原子路径)。

如果目标文件路径存在,rename将自动将其从文件系统中删除,并将其替换为新文件。在引用计数降至零之前,文件实际上不会被删除,因此如果另一个进程当前正在读取该文件,它将继续读取旧文件。一旦所有进程都关闭了旧文件,其引用计数将降至零,并且将回收文件存储空间。

答案 1 :(得分:5)

为什么不使用FileChannel.lock

这是一个例子:

http://examples.javacodegeeks.com/core-java/nio/filelock/create-shared-file-lock-on-file/

答案 2 :(得分:3)

  1. 如果在同一设备上完成,则(重命名)是原子的。 (device =相同的磁盘/分区)
  2. 如果Foo.txt退出移动Foo.tmpFoo.txt,则很可能会失败。 (但是如果你先删除Foo.txt然后移动,它应该可以工作)。发生的情况是,在关闭所有文件处理程序(没有使用该文件的进程)之前,文件不会被物理删除。此外,在保留Foo.tmpFoo.txt之后,您将拥有2个Foo.txt文件。一个被删除但仍在内存中打开的(基本上该文件在磁盘上没有引用)和一个实际驻留在磁盘上的文件。
  3. 但是,移动后,在第二个过程中,您需要重新打开该文件。
  4. 如果我们与#1在同一页上,请告诉我。