FileAlreadyExistsException未被抛出,并覆盖文件

时间:2016-10-04 23:31:26

标签: java multithreading file io

此代码:

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。

不会抛出任何异常,并且文件的数据已被覆盖。

2 个答案:

答案 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,在使用多个线程时状态可能会发生变化。