【问题标题】:FileAlreadyExistsException not being thrown, and overwriting fileFileAlreadyExistsException 没有被抛出,并覆盖文件
【发布时间】:2017-02-13 06:24:50
【问题描述】:

这段代码:

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 [警告] [_Executor-1] a.b.c.MyGenerator - 文件夹中已存在文件“myFile_01092016103511.csv” '/myDir': 在生成时间上增加一秒

2016-10-04 10:35:11,677 [警告] [_Executor-3] a.b.c.MyGenerator - 文件夹中已存在文件“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 - 临时文件“tmp6843688962754506611.tmp”重命名为 'myFile_01092016103512.csv'

Executor 3 覆盖文件,即使它应该抛出 FileAlreadyExistsException。

没有抛出异常,文件的数据已经被覆盖。

【问题讨论】:

    标签: java multithreading file io


    【解决方案1】:

    Executor 3 会覆盖文件,即使它应该抛出 FileAlreadyExistsException。

    如果您希望Files.move(...) 以原子方式运行,则需要使用ATOMIC_MOVE option。默认情况下,它首先测试目标是否存在,然后执行易受thread race conditions 影响的移动。这里是javadocs for Files.move(...)

    ATOMIC_MOVE – 将移动作为原子文件操作执行。如果文件系统不支持原子移动,则会引发异常。使用 ATOMIC_MOVE,您可以将文件移动到目录中,并保证观察该目录的任何进程都可以访问完整的文件。

    如果您必须拥有FileAlreadyExistsException 支持,那么您将需要使用synchronized 块来保护移动,以便一次只允许一个线程运行它。鉴于您在此块中执行 IO,synchronized 应该几乎没有性能损失。

    【讨论】:

      【解决方案2】:

      Files.move 内部使用Files.exists,使用多个线程时状态可能在两者之间发生变化。

      【讨论】:

        猜你喜欢
        • 2019-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-08
        • 1970-01-01
        • 2013-01-25
        相关资源
        最近更新 更多