File.createNewFile() случайно терпит неудачу

Я создал простой тест, который создает и удаляет файл (имя не меняется) в бесконечном цикле. Тест выполняется пару секунд (иногда более 77 000 итераций!), а затем завершается ошибкой с этим исключением:

Exception in thread "main" java.io.IOException: Access is denied
        at java.io.WinNTFileSystem.createFileExclusively(Native Method)
        at java.io.File.createNewFile(Unknown Source)
        at DeleteTest.main(DeleteTest.java:11)

Вот логика теста:

final File f = new File(pathname);
while (true) {
    final boolean create = f.createNewFile();
    if (!create) {
        System.out.println("crate failed");
    } else {
        final boolean delete = f.delete();
        if (!delete) {
            System.out.println("delete failed");
        }
    }
}

Как это возможно? Вызов удаления не терпит неудачу. Это бы сказало. Таким образом, удалить всегда удается, но createNewFile не удается. Это то, что MSDN говорит о функции Win32 API DeleteFile:

Функция DeleteFile помечает файл для удаления при закрытии. Поэтому удаление файла не происходит до тех пор, пока не будет закрыт последний дескриптор файла. Последующие вызовы CreateFile для открытия файла завершаются ошибкой ERROR_ACCESS_DENIED.

Значит createNewFile не закрывает файл? Источник openjdk сообщает нам, что файл закрыт:

JNIEXPORT jboolean JNICALL
Java_java_io_Win32FileSystem_createFileExclusively(JNIEnv *env, jclass cls,
                                                   jstring pathname)
{
    jboolean rv = JNI_FALSE;
    DWORD a;

    WITH_PLATFORM_STRING(env, pathname, path) {
        int orv;
        int error;
        JVM_NativePath((char *)path);
        orv = JVM_Open(path, JVM_O_RDWR | JVM_O_CREAT | JVM_O_EXCL, 0666);
        if (orv < 0) {
            if (orv != JVM_EEXIST) {
                error = GetLastError();

                // If a directory by the named path already exists,
                // return false (behavior of solaris and linux) instead of
                // throwing an exception
                a = GetFileAttributes(path);

                if ((a == INVALID_FILE_ATTRIBUTES) ||
                        !(a & FILE_ATTRIBUTE_DIRECTORY)) {
                    SetLastError(error);
                    JNU_ThrowIOExceptionWithLastError(env, path);
                }
            }
        } else {
            JVM_Close(orv);
            rv = JNI_TRUE;
        }
    } END_PLATFORM_STRING(env, path);
    return rv;
}

Кто-нибудь может объяснить такое поведение?


person Eduard Wirch    schedule 09.05.2012    source источник
comment
stackoverflow.com/a/23697734/715269   -  person Gangnus    schedule 16.05.2014
comment
@Gangnus, я прямо заявил, что поведение было случайным. Итак: нет, это не проблема разрешения.   -  person Eduard Wirch    schedule 21.05.2014
comment
Я понимаю. Я помещаю ответ здесь только потому, что искал решение подобной проблемы, и ваша была одной из страниц, которые я читал, и когда я нашел решение, я разместил здесь ссылку, чтобы помочь кому-то в будущем. Это НЕ подразумевается как ответ или ответ на ваш вопрос, и он также не публикуется как ответ.   -  person Gangnus    schedule 21.05.2014


Ответы (3)


Я нашел объяснение, когда писал вопрос. Я все же опубликовал вопрос, потому что хотел поделиться тем, что узнал.

Мое приложение — не единственный процесс в системе, обращающийся к файлам. Служба индекса поиска Windows, например, может открыть этот файл, потому что хочет добавить его в свой индекс. Или проводник Windows, если он обновляет вид.

person Eduard Wirch    schedule 09.05.2012
comment
В вашем случае могу поспорить, что это ваше антивирусное программное обеспечение. Такого рода вещи происходят все время с av. - person David Heffernan; 09.05.2012
comment
В старой службе индексации содержимого Windows была неприятная ошибка в этой области. Если вы быстро создали и удалили каталог, он откроет дескриптор каталога (предположительно с помощью FindFirstFile), но не сможет его закрыть, оставив каталог-зомби, с которым вы ничего не сможете сделать. Единственный способ убить зомби — перезапустить службу индексации. - person arx; 09.05.2012

Попробуй это:

final File f = new File("file");
    while (true) {
        final boolean create = f.createNewFile();
        if (!create) {
            System.out.println("crate failed");
        } else {
            final boolean delete = f.delete();
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                System.out.println("...");
            }
            if (!delete) {
                System.out.println("delete failed");
            }
        }
    }

Таким образом, мы гарантируем, что файл освобождается при удалении перед вызовом createNewFile.

person Diego D    schedule 09.05.2012
comment
Что ж, ваш код снижает вероятность неудачи, но не гарантирует успеха. Может быть еще процесс, который держит файл открытым дольше 10 мс. - person Eduard Wirch; 09.05.2012
comment
Обратите внимание, что команда на удаление файла была отдана ОС, после того как ОС обработает эту команду, никто не должен ее блокировать. - person Diego D; 09.05.2012
comment
Если вы хотите быть уверенным, перехватите java.io.IOException в f.createNewFile(), а затем подождите 10 мс, после чего повторите попытку. - person Diego D; 09.05.2012

Эта проблема напоминает мне проблему, с которой я недавно столкнулся при использовании метода File.renameTo(). Это (было?) из-за этой ошибки в jvm:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6213298

Странный обходной путь — вызвать System.gc() и снова попытаться переименовать файл (и это работает...).

Не уверен, что это связано с вашей проблемой, но, возможно, стоит изучить...

person Yanflea    schedule 09.05.2012