我有一组机器,每台都运行一个Java应用程序。
这些Java应用程序需要能够快速访问唯一的resource.txt
文件。
我需要在Java中以原子方式将temp.txt
文件重命名为resource.txt
,即使resource.txt
已经存在。
删除resource.txt
并重命名temp.txt
不起作用,因为它不是原子的(它创建了一个不存在resource.txt
的小时间段。)
它应该是跨平台的......
谢谢!
答案 0 :(得分:32)
对于Java 1.7+,请将java.nio.file.Files.move(Path source, Path target, CopyOption... options)
与CopyOptions“REPLACE_EXISTING”和“ATOMIC_MOVE”一起使用。
See API documentation for more information.
例如:
Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE);
答案 1 :(得分:13)
在Linux上(我相信Solaris和其他UNIX操作系统),Java的File.renameTo()方法将覆盖目标文件(如果存在),但在Windows下不是这种情况。
要跨平台,我认为您必须在resource.txt上使用文件锁定,然后覆盖数据。
文件锁的行为是 平台依赖性。在某些平台上, 文件锁是咨询,这意味着 除非申请检查 文件锁定,不会被阻止 从访问该文件。在其他方面 平台,文件锁是必需的, 这意味着文件锁阻止 访问的任何应用程序 文件。
try {
// Get a file channel for the file
File file = new File("filename");
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
// Use the file channel to create a lock on the file.
// This method blocks until it can retrieve the lock.
FileLock lock = channel.lock();
// Try acquiring the lock without blocking. This method returns
// null or throws an exception if the file is already locked.
try {
lock = channel.tryLock();
} catch (OverlappingFileLockException e) {
// File is already locked in this thread or virtual machine
}
// Release the lock
lock.release();
// Close the file
channel.close();
} catch (Exception e) {
}
默认情况下,Linux使用自动锁定,而Windows会强制执行。也许你可以检测到操作系统,并在UNIX下使用renameTo()和Windows的某些锁定代码?
还有一种方法可以在Linux下为特定文件启用强制锁定,但这有点模糊。你必须正确设置模式位。
Linux,遵循System V(参见系统 V接口定义(SVID)版本 3),让sgid位为文件 没有组执行权限标记 强制锁定的文件
答案 2 :(得分:6)
答案 3 :(得分:2)
如上所述here,看起来Windows操作系统甚至不支持旧版本的原子文件重命名。您很可能必须使用某些手动锁定机制或某种交易。为此,您可能需要查看apache commons transaction包。
答案 4 :(得分:1)
如果这应该是跨平台的,我建议2个选项:
答案 5 :(得分:1)
如果重命名的目的是动态替换resource.txt 和,您可以控制所涉及的所有程序,和的替换频率不高,你可以做到以下几点。
打开/阅读文件:
替换文件:
这将确保所有读者始终找到有效的文件。
但是,更简单的是,只需在循环中尝试打开,例如:
InputStream inp=null;
StopWatch tmr=new StopWatch(); // made up class, not std Java
IOException err=null;
while(inp==null && tmr.elapsed()<5000) { // or some approp. length of time
try { inp=new FileInputStream("resource.txt"); }
catch(IOException thr) { err=thr; sleep(100); } // or some approp. length of time
}
if(inp==null) {
// handle error here - file did not turn up after required elapsed time
throw new IOException("Could not obtain data from resource.txt file");
}
... carry on
答案 6 :(得分:1)
通过在重命名文件之前在文件上建立文件通道锁定(以及删除锁定后要覆盖的文件),可能会获得一些牵引力。 -r
答案 7 :(得分:0)
我用一个简单的重命名功能解决了
通话:
File newPath = new File("...");
newPath = checkName(newPath);
Files.copy(file.toPath(), newPath.toPath(), StandardCopyOption.REPLACE_EXISTING);
checkName函数检查是否退出。 如果退出,则在文件名末尾的两个方括号(1)之间连接一个数字。 功能:
private static File checkName(File newPath) {
if (Files.exists(newPath.toPath())) {
String extractRegExSubStr = extractRegExSubStr(newPath.getName(), "\\([0-9]+\\)");
if (extractRegExSubStr != null) {
extractRegExSubStr = extractRegExSubStr.replaceAll("\\(|\\)", "");
int parseInt = Integer.parseInt(extractRegExSubStr);
int parseIntPLus = parseInt + 1;
newPath = new File(newPath.getAbsolutePath().replace("(" + parseInt + ")", "(" + parseIntPLus + ")"));
return checkName(newPath);
} else {
newPath = new File(newPath.getAbsolutePath().replace(".pdf", " (" + 1 + ").pdf"));
return checkName(newPath);
}
}
return newPath;
}
private static String extractRegExSubStr(String row, String patternStr) {
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(row);
if (matcher.find()) {
return matcher.group(0);
}
return null;
}
编辑:它仅适用于pdf。如果需要其他文件,请替换.pdf或为其创建扩展名参数。 注意:如果文件在方括号“(”之间包含其他数字,则可能会弄乱您的文件名。