文件系统更新,锁定,时序问题

时间:2018-03-06 03:03:52

标签: java windows file-locking

使用Java的java.nio.channels.FileLock我试图在Windows文件系统上同步文件读写。我有一个循环运行的测试程序:

  1. 锁定文件X.LOCK
  2. 测试X.JSON是否存在(只是一致性检查)
  3. 写入文件X.TMP
  4. 使用java.nio.files.Files.move()将X.TMP重命名为X.JSON,删除旧的X.JSON并在原子操作中将X.TMP重命名为X.JSON。
  5. 测试X.JSON是否存在(这总是返回true)
  6. 解除对X.LOCK的锁定
  7. 我在测试程序的多个实例中以紧密循环运行它。它锁定了符号文件" X.LOCK"而不是正在写入和重命名的实际文件。我认为有必要通过重命名操作来保持锁定。

    这是我发现的:在大约2%的情况下,进程1将写入/重命名/释放锁,而等待该锁的进程2将获得该锁,开始执行,但发现 X.JSON不存在。 "存在" check返回false!

    如果我在重命名之后和解锁之前引入延迟(200ms),那么整个事情可靠地运行100%。我可以尝试较小的延迟,但我不愿意添加任何延迟,因为这绝不是制作可靠程序的正确答案。

    似乎当一个进程以原子方式重命名文件时,其他进程需要一些时间才能看到该文件。但解锁信号更快!因此锁定信号告诉其他程序向前移动,而其他程序无法看到它应该正在处理的文件!

    问题:有没有什么方法可以强制解锁信号在文件系统稳定后发送,并保证与在调用解锁之前放在那里的操作一致?

    有关使用Java在Windows文件系统上查找有关此类时序/排序信息的任何提示?我还没有在任何其他平台上尝试过这个测试程序,但我很快就会检查Linux。

    更新

    我怀疑病毒扫描造成的干扰。它对可重现的状态进行了测试,并且它在大约1%的时间内失败,这次报告" AccessDeniedException"。我认为病毒扫描可能正在进行,在创建和重命名之间扫描文件,当它执行此操作时,它以更高的权限运行,并在尝试重命名时导致此错误。其他人遇到这个问题?

1 个答案:

答案 0 :(得分:1)

解决方案似乎是在运行病毒扫描的系统上,根据病毒扫描程序的特定品牌,移动调用可能会受到干扰。我在打电话:

java.nio.files.Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING, 
           StandardCopyOption.ATOMIC_MOVE );

此命令将有效删除dest(如果存在),并将src文件重命名为dest,它将以原子方式执行。据记载,如果它不能原子地执行,它将抛出异常。我得到了AccessDeniedException,这在文档中没有具体提到,但显然已经发生了。

似乎正在发生的事情 - 这一切都取决于大约1%的时间发生的特定时间 - 是病毒的操作扫描在src文件上还是dest文件导致原子移动失败。

我尝试了三种不同配置的系统。带有Microsoft Windows Defender的Windows计算机从未导致AccessDeniedException而另一台使用趋势科技病毒扫描的计算机定期失败。这不是对病毒扫描选项的彻底调查;它们是我可用于测试的唯一选择。趋势科技的机器也有一个加密的硬盘,这可能是制定时机的一个因素,例如解决这个问题。

我甚至实现了“重试”,如果移动引发异常,代码将等待10ms并再次尝试。即便如此,重试失败的时间约占0.1%。也许我可以等待更长时间,但无论如何这都会导致代码变慢。

在移动之前添加删除要替换的文件的步骤是有效的。我的猜测是,删除会阻止病毒扫描,否则它会继续扫描srcdest文件,而不会打扰move命令。步骤如下:

  1. 锁定文件X.LOCK
  2. 测试X.JSON是否存在(只是一致性检查)
  3. 写入文件X.TMP
  4. (新)删除旧的X.JSON
  5. 使用java.nio.files.Files.move()将X.TMP重命名为X.JSON,只需在原子动作中将X.TMP重命名为X.JSON。
  6. 测试X.JSON是否存在(这总是返回true)
  7. 解除对X.LOCK的锁定
  8. 这现在100%可靠吗?我不能肯定地说,因为这一切都取决于时间。这可能只是以允许它运行的方式改变了时间。