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

Tho*_*ing 7 java multithreading atomic java.nio.file

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

Files.move在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        }
Run Code Online (Sandbox Code Playgroud)

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

如果移动无法作为原子文件系统操作执行,则抛出AtomicMoveNotSupportedException.例如,当目标位置位于不同的FileStore上并且需要复制文件,或者目标位置与该对象的不同提供者相关联时,可能会出现这种情况.

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

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

Hol*_*ger 8

你正在寻找错误的地方.当文件系统提供程序不相同时,将moveToForeignTarget按照您在已发布的代码段中看到的方式委派操作.moveToForeignTarget然而,该方法将使用该方法convertMoveToCopyOptions(注意说话名称......)来获得翻译操作的必要复制选项.并且如果遇到该选项convertMoveToCopyOptions将抛出一个AtomicMoveNotSupportedException,ATOMIC_MOVE因为无法将该移动选项转换为有效的复制选项.

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

  • @Thorsten Schöning:测试不是最后的结论,但使用两个不同文件系统的单个测试将反驳“永远不会”抛出异常的假设,并揭示抛出异常的位置。因此,错误报告通常会添加可以重现错误的示例代码。没关系,这种事情发生了,问问总比盲目相信要好…… (2认同)