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

2022-09-01 17:01:25

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

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

所以我认为我可以让P1写入,作为最后一步,重命名为。我的编程语言是JavaFoo.tmpFoo.tmpFoo.txt

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

编辑

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

我的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内容,直到它重新打开缓冲阅读器到Foo.txt。

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

P2 输出:

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

所以它不能正常工作。我是否错误地测试了此方案?我觉得我错过了一些东西。


答案 1

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

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


答案 2

为什么不使用?FileChannel.lock

下面是一个示例:

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


推荐