How to atomically rename a file in Java,even if the dest file already exists?建议重命名文件是“原子操作”(无论“原子”实际上是什么意思).
https://stackoverflow.com/a/20570968/65458建议编写tmp文件并重命名是跨平台的,并确保最终文件不存在或可由其他进程处理.
所以我试着实际实现这种方法.以下是我的尝试摘要.对于实际问题 – 跳到底部.
写方法
我尝试了各种编写和重命名文件的方法(内容和字符集分别是String和Charset):
使用java.nio.file.Files:
Files.copy(new ByteArrayInputStream(content.getBytes(charset)),tmpFile); Files.move(tmpFile,finalFile,StandardCopyOption.ATOMIC_MOVE);
使用Guava(14)和java.io.File:
com.google.common.io.Files.write(content,tmpFile,charset); tmpFile.renameTo(finalFile);
或者甚至更模糊的方法:
try (OutputStream os = new FileOutputStream(tmpFile); Writer writer = new OutputStreamWriter(os,charset)) { writer.write(content); } Runtime.getRuntime().exec( new String[] { "cmd.exe","/C","move " + tmpFile + " " + finalFile }).waitFor();
阅读方法
现在假设另一个线程(线程因为我在测试中,在现实生活中它可能是另一个进程)正在执行以下版本的代码之一:
具有共同功能:
void waitUntilExists() throws InterruptedException { while (!java.nio.file.Files.exists(finalFile)) { NANOSECONDS.sleep(1); } }
使用java.nio.file.Files:
waitUntilExists(); return new String(Files.readAllBytes(finalFile),charset);
使用番石榴(14):
waitUntilExists(); return new String(com.google.common.io.Files.toByteArray(finalFile.toFile()),charset);
或者甚至更模糊的方法:
waitUntilExists(); StringBuilder sb = new StringBuilder(); try (InputStream is = new FileInputStream(finalFile.toFile())) { byte[] buf = new byte[8192]; int n; while ((n = is.read(buf)) > 0) { sb.append(new String(buf,n,charset)); } } return sb.toString();
结果
如果我使用“java.nio.file.Files方法”阅读,一切正常.
如果我在Linux上运行此代码(超出此问题的范围,我知道),一切正常.
但是,如果我使用Guava或FileInputStream实现读取,那么可能性高于0.5%(0.005),测试失败
java.io.FileNotFoundException: Process cannot access the file,because it is being used by another process
(由我自己翻译的消息导致我的窗口不是英文;提到“另一个进程”是误导性的,因为即使这是相同的过程,Windows也是正常的,我用明确的阻止验证了.)
题
如何在Windows上使用Java实现create-then-rename,以便最终文件以原子方式显示,即要么不存在,要么可以读取?
因为我对进程的控制比拾取文件所控制的,我不能假设使用任何特定的读取方法,或者甚至它们都是Java.因此,该解决方案应适用于上面列出的所有读取方法.
此外,使用旧IO和NIO的读取之间的行为差异可能是因为它们使用不同的Windows API.
For applications that use the file read/write APIs in Windows,
byte-range locks are enforced (also referred to as mandatory locks) by
the file systems that execute within Windows. For applications that
use the file mapping APIs in Windows,byte-range locks are not
enforced (also referred to as advisory locks.)
虽然维基百科不是Windows的文档,但这仍然有所启发.
(我只是把这个答案放在一起,以便其他人也不必写这个.真正的答案,参考文档或报告的错误,非常感谢.)