24

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

假设 P2 正在读取Foo.txt. 我不希望 P1Foo.txt在 P2 读取时写入。

所以我想我可以让 P1 写入Foo.tmp并作为最后一步,重命名Foo.tmpFoo.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
 .
 .
 .

所以它不能正常工作。我测试这个场景错了吗?我觉得我错过了什么。

4

3 回答 3

38

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

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

于 2013-09-09T20:40:41.160 回答
5

为什么不使用FileChannel.lock

这是一个例子:

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

于 2013-09-09T20:29:33.020 回答
3
  1. 如果在同一设备上完成 move(rename) 是原子的。(设备 = 相同的磁盘/分区)
  2. 如果Foo.txt出口移动Foo.tmpFoo.txt最有可能会失败。(但如果你先删除Foo.txt然后移动,它应该可以工作)。发生的情况是,直到所有文件处理程序都关闭(没有使用该文件的进程),文件才被物理删除。此外,剩下Foo.tmpFoo.txt你将有 2 个 Foo.txt 文件。一个被删除但仍在内存中打开(基本上该文件在磁盘上不再有引用)和一个实际驻留在磁盘上的文件。
  3. 但是,在移动之后,在第二个过程中您需要重新打开文件。

让我知道我们是否与#1 在同一页面上。

于 2013-09-09T20:24:33.727 回答