Pio*_*sen 7 java windows file-io atomicity
我正在尝试在Windows上使用Java 正确实现"写临时文件并重命名" .
如何在Java中以原子方式重命名文件,即使dest文件已存在?建议重命名文件是"原子操作"(无论"原子"实际上是什么意思). /sf/answers/1439967791/建议编写tmp文件并重命名是跨平台的,并确保最终文件不存在或可由其他进程处理.
所以我试着实际实现这种方法.以下是我的尝试摘要.对于实际问题 - 跳到底部.
我试着写和重命名文件的各种方法(content和charset是String和Charset分别对应).
使用java.nio.file.Files:
Files.copy(new ByteArrayInputStream(content.getBytes(charset)), tmpFile);
Files.move(tmpFile, finalFile, StandardCopyOption.ATOMIC_MOVE);
Run Code Online (Sandbox Code Playgroud)
使用番石榴(14)和java.io.File:
com.google.common.io.Files.write(content, tmpFile, charset);
tmpFile.renameTo(finalFile);
Run Code Online (Sandbox Code Playgroud)
或者甚至更模糊的方法:
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();
Run Code Online (Sandbox Code Playgroud)
现在假设另一个线程(线程因为我在测试中,在现实生活中它可能是另一个进程)正在执行以下版本的代码之一:
具有共同功能:
void waitUntilExists() throws InterruptedException {
while (!java.nio.file.Files.exists(finalFile)) {
NANOSECONDS.sleep(1);
}
}
Run Code Online (Sandbox Code Playgroud)
使用java.nio.file.Files:
waitUntilExists();
return new String(Files.readAllBytes(finalFile), charset);
Run Code Online (Sandbox Code Playgroud)
使用番石榴(14):
waitUntilExists();
return new String(com.google.common.io.Files.toByteArray(finalFile.toFile()), charset);
Run Code Online (Sandbox Code Playgroud)
或者甚至更模糊的方法:
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, 0, n, charset));
}
}
return sb.toString();
Run Code Online (Sandbox Code Playgroud)
如果我使用" 方法" 阅读java.nio.file.Files,一切都运行正常.
如果我在Linux上运行此代码(超出此问题的范围,我知道),一切正常.
但是,如果我用Guava 实现读取,或者FileInputStream可能性高于0.5%(0.005),则测试失败
java.io.FileNotFoundException:进程无法访问该文件,因为它正由另一个进程使用
(由我自己翻译的消息导致我的窗口不是英文;提到"另一个进程"是误导性的,因为即使这是相同的过程,Windows也是正常的,我用明确的阻止验证了.)
如何在Windows上使用Java实现create-then-rename,以便最终文件以原子方式显示,即要么不存在,要么可以读取?
因为我对进程的控制比拾取文件所控制的,我不能假设使用任何特定的读取方法,或者甚至它们都是Java.因此,该解决方案应适用于上面列出的所有读取方法.
这似乎就是 Windows/NTFS 的行为方式。
此外,使用旧 IO 和 NIO 进行读取的行为差异可能是因为它们使用不同的 Windows API。
对于在 Windows 中使用文件读/写 API 的应用程序,字节范围锁由 Windows 中执行的文件系统强制执行(也称为强制锁)。对于在 Windows 中使用文件映射 API 的应用程序,不强制执行字节范围锁(也称为咨询锁)。
虽然维基百科不是 Windows 的文档,但这仍然提供了一些线索。
(我提出这个答案只是为了让其他有同样想法的人不必写这个。真正的答案,参考文档或报告的错误,非常感谢。)
| 归档时间: |
|
| 查看次数: |
1504 次 |
| 最近记录: |