此代码:
File tmpFile = File.createTempFile(PREFIX_TMP, null, new File(reportPath));
logger.debug("The report will be saved in the temporary file '{}'", tmpFile.getName());
reportWriter.write(tmpFile, report);
Calendar calendar = Calendar.getInstance();
boolean isFileMoved = false;
while (!isFileMoved) {
String reportName = String.format(report.getName(), calendar.getTime());
File reportFile = new File(reportPath, reportName);
try {
Files.move(tmpFile.toPath(), reportFile.toPath());
isFileMoved = true;
logger.info("The temporary file '{}' is renamed to '{}'", tmpFile.getName(), reportFile.getName());
} catch (FileAlreadyExistsException e) {
logger.warn("The file '{}' already exists in the folder '{}': adding a second to generation time",
reportName, reportPath);
}
calendar.add(Calendar.SECOND, 1);
}
生成以下不可能的日志语句:
2016-10-04 10:35:11,674 [WARN] [_Executor-1] a.b.c.MyGenerator - The 文件'myFile_01092016103511.csv'已存在于该文件夹中 '/ myDir':增加第二代生成时间
2016-10-04 10:35:11,677 [WARN] [_Executor-3] a.b.c.MyGenerator - The 文件'myFile_01092016103511.csv'已存在于该文件夹中 '/ myDir':增加第二代生成时间
2016-10-04 10:35:11,677 [INFO] [_Executor-1] a.b.c.MyGenerator - The 临时文件'tmp2103892505851032137.tmp'被重命名为 'myFile_01092016103512.csv'
2016-10-04 10:35:11,680 [INFO] [_Executor-3] a.b.c.MyGenerator - The 临时文件'tmp6843688962754506611.tmp'被重命名为 'myFile_01092016103512.csv'
执行程序3会覆盖文件,即使它应该抛出FileAlreadyExistsException。
不会抛出任何异常,并且文件的数据已被覆盖。
答案 0 :(得分:1)
执行程序3会覆盖该文件,即使它应该抛出FileAlreadyExistsException。
如果您希望Files.move(...)
以原子方式行动,则需要使用ATOMIC_MOVE
option。默认情况下,它首先测试目标是否存在,然后执行易受thread race conditions影响的移动。以下是javadocs for Files.move(...)
:
ATOMIC_MOVE - 将移动作为原子文件操作执行。如果文件系统不支持原子移动,则抛出异常。使用ATOMIC_MOVE,您可以将文件移动到目录中,并保证观察目录的任何进程都可以访问完整的文件。
如果您必须拥有FileAlreadyExistsException
支持,则需要使用synchronized
块保护移动,以便一次只允许一个线程运行它。鉴于您在此块中执行IO,synchronized
应该很少甚至没有性能损失。
答案 1 :(得分:0)
Files.move
在内部使用Files.exists
,在使用多个线程时状态可能会发生变化。