如何保证Java中文件的原子移动或异常?

时间:2014-08-11 07:54:29

标签: java multithreading atomic java.nio.file

在我的一个项目中,我对一个JRE中的一个文件具有并发写入权限,并希望通过首先写入临时文件并使用原子移动将该临时文件移动到目标来处理该文件。我不关心写访问的顺序等等,我需要保证的是单个文件可用的任何给定时间。我已经知道Files.move等等,我的问题是我看了一下该方法的至少一个实现,并且它引发了一些疑问,即实现是否真的能保证原子移动。请查看以下代码:

Files.move on GrepCode for OpenJDK

1342        FileSystemProvider provider = provider(source);
1343        if (provider(target) == provider) {
1344            // same provider
1345            provider.move(source, target, options);
1346        } else {
1347            // different providers
1348            CopyMoveHelper.moveToForeignTarget(source, target, options);
1349        }

问题是在所有情况下都不考虑选项ATOMIC_MOVE,但源和目标路径的位置是唯一重要的事情。这不是我想要的以及我对文档的理解:

  

如果移动不能作为原子文件系统操作那么执行   抛出AtomicMoveNotSupportedException。例如,当目标时,这可能出现   location位于不同的FileStore上,需要复制或定位文件   location与此对象的不同提供者相关联。

上面的代码明显违反了该文档,因为它完全依赖于复制删除策略而根本没有识别ATOMIC_MOVE。在我的情况下,一个例外是完全可以的,因为我们的服务的主机可以改变他的设置只使用一个支持原子移动的文件系统,因为这正是我们对系统要求的期望。我不想处理的是静默失败的事情,因为实现使用了复制删除策略,这可能导致目标文件中的数据损坏。因此,根据我的理解,依赖Files.move进行原子操作是不安全的,因为如果不支持这些操作,它并不总是会失败,但实现可能会回归到复制删除策略。

此类行为是否是实施中的错误,需要提交或文档是否允许此类行为,我理解错误?如果我现在已经知道那些可能已经破坏的实现在那里使用了,它会有什么不同吗?在这种情况下,我需要自己同步写访问...

2 个答案:

答案 0 :(得分:8)

你看错了地方。当文件系统提供程序不相同时,操作将委派给moveToForeignTarget,如您在已发布的代码段中所见。然而,方法moveToForeignTarget将使用方法convertMoveToCopyOptions(注意说话名称...)来获取翻译操作的必要复制选项。如果遇到convertMoveToCopyOptions选项,则AtomicMoveNotSupportedException会抛出ATOMIC_MOVE,因为无法将该移动选项转换为有效的复制选项。

所以没有理由担心,一般来说,建议避免因看到不到十行代码而导致仓促结论(特别是在没有尝试过单一测试的情况下)......

答案 1 :(得分:2)

标准Java库并未提供在所有情况下执行原子移动的方法。

Files.move()不保证原子移动。您可以将ATOMIC_MOVE作为选项传递,但如果移动不能作为原子操作执行,则抛出AtomicMoveNotSupportedException(当目标位置位于不同的FileStore上并且需要该文件时就是这种情况被复制)。

如果你确实需要,你必须自己实施。一种解决方案可以是捕获AtomicMoveNotSupportedException,然后执行此操作:尝试在没有ATOMIC_MOVE选项的情况下移动文件,但捕获异常并在复制期间发生错误时删除目标。