Java 同时创建新文件

Java 同时创建新文件,java,windows,multithreading,file,Java,Windows,Multithreading,File,要创建新的唯一文件名,我使用以下代码: File file = new File(name); synchronized (sync) { int cnt = 0; while (file.exists()) { file = new File(name + " (" + (cnt++) + ")"); } file.createNewFile(); } 接下来,我使用该文件并将其删除。 在多线程情况下执行此操作时,有时会在文件.createNe

要创建新的唯一文件名,我使用以下代码:

File file = new File(name);
synchronized (sync) {
    int cnt = 0;
    while (file.exists()) {
        file = new File(name + " (" + (cnt++) + ")");
    }
    file.createNewFile();
}
接下来,我使用该文件并将其删除。 在多线程情况下执行此操作时,有时会在
文件.createNewFile()
上出现异常:

以下代码再现了问题(大多数情况下):

当我执行这段代码时,它有时运行良好,但大多数情况下,它会生成一些异常,例如下面的输出:

Runnable 1: exception after 235 runs: Access is denied
java.io.IOException: Access is denied
    at java.io.WinNTFileSystem.createFileExclusively(Native Method)
    at java.io.File.createNewFile(File.java:1012)
    at test.CreateFilesTest$1.run(CreateFilesTest.java:36)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Runnable 4: exception after 316 runs: Access is denied
java.io.IOException: Access is denied
    at java.io.WinNTFileSystem.createFileExclusively(Native Method)
    at java.io.File.createNewFile(File.java:1012)
    at test.CreateFilesTest$1.run(CreateFilesTest.java:36)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Runnable 2: exception after 327 runs: Access is denied
java.io.IOException: Access is denied
    at java.io.WinNTFileSystem.createFileExclusively(Native Method)
    at java.io.File.createNewFile(File.java:1012)
    at test.CreateFilesTest$1.run(CreateFilesTest.java:36)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Runnable 3 finished fine
Runnable 0 finished fine
有什么想法吗?我在Windows8机器上用Java1.7.0_45和1.8.0_31进行了测试,结果都是一样的


不确定问题是否与中相同,但可以。在我看来,在同一个进程中使用多个线程似乎是问题的一部分,但我不能确定这一点,它的复制速度更快。

您的循环不是故障安全的。时间窗有问题。应该是这样的:

while (!file.createNewFile()) {
        file = new File(name + " (" + (cnt++) + ")");
    }
synchronized (sync) {
    int cnt = 0;
    while (true) {
        try {
            if(file.createNewFile())
                break;
        } catch (IOException e) {
            // continue;
        }
        file = new File(name + " (" + (cnt++) + ")");
    }
}

似乎在Windows平台上,即使在单线程应用程序上删除同名文件,createNewFile也可能会随机失败。有关详细信息,请参阅。要解决此问题,您可以尝试忽略
createNewFile
中的
IOException
,然后继续。大概是这样的:

while (!file.createNewFile()) {
        file = new File(name + " (" + (cnt++) + ")");
    }
synchronized (sync) {
    int cnt = 0;
    while (true) {
        try {
            if(file.createNewFile())
                break;
        } catch (IOException e) {
            // continue;
        }
        file = new File(name + " (" + (cnt++) + ")");
    }
}
注意,您不需要检查
file.exists()
调用
createNewFile()
即可方便地返回它是否成功创建了文件


请注意,如果您控制创建的所有临时文件,而不关心确切的文件名,则通常不需要锁定。您可以使用全局
AtomicLong
来获取下一个文件名或在文件名中附加线程ID。

file.createTempFile()
在这里可能是一种更干净的方法,而不管该文件实际上是否是临时文件。@Sneftel:我同意,文件名很重要,因此我不能在此处使用file.createTempFile。请您详细说明此同步部分中的“计时窗口”是什么?您的固定代码也会以与OPs代码相同的方式失败。@TagirValeev在
exists()
createNewFile()
之间有一个计时窗口,在此期间,文件可以由另一个线程创建。检查
createNewFile()
的结果也失败。如果我在这里发布的两行循环失败,那么这个方法肯定存在平台问题,不能像宣传的那样工作。事实上,这个循环和答案中的代码之间没有区别,除了catch块。如何解释?请注意,所有线程都在同一个监视器上同步,因此其他线程无法执行此操作。@TagirValeev,以便其他进程可以创建它。还有一个时间窗问题。因为这个问题可能是由外部因素引起的,所以我想在这种情况下我会设法摆脱这个问题
synchronized (sync) {
    int cnt = 0;
    while (true) {
        try {
            if(file.createNewFile())
                break;
        } catch (IOException e) {
            // continue;
        }
        file = new File(name + " (" + (cnt++) + ")");
    }
}