2012-05-09 3 views
1

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

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 функции апи DeleteFile:

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

So 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; 
} 

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

+0

http://stackoverflow.com/a/23697734/715269 – Gangnus

+0

@Gangnus, я явно указал, что поведение случайным образом. Итак: нет, это не проблема разрешения. –

+0

Я вижу. Я помещаю ответ здесь только потому, что искал решение подобной проблемы, и ваша была одной из страниц, которые я читал, и когда я нашел решение, я добавил ссылку для помощи кому-то в будущем. Это НЕ означает ответ или ответ на ваш вопрос, и он также не публикуется в качестве ответа. – Gangnus

ответ

2

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

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

+1

В вашем случае я уверен, что это ваше антивирусное программное обеспечение. Такое происходит всегда с av. –

+1

У старой службы индексирования контента Windows была неприятная ошибка в этой области.Если вы быстро создали и удалили каталог, он откроет дескриптор каталога (предположительно используя FindFirstFile), но не сможет его закрыть, оставив каталог zombie, с которым вы ничего не могли бы сделать. Единственный способ убить зомби - перезапустить службу индексирования. – arx

0

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

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.

+0

Ну, ваш код снижает вероятность неудачи, но это не гарантирует успех. Может быть еще процесс, который держит файл открытым дольше 10 мс. –

+0

Обратите внимание, что порядок удаления файла был предоставлен ОС, после того как ОС обработает эту команду, никто не должен ее блокировать. –

+0

Если вы хотите быть уверенным, поймайте java.io.IOException в f.createNewFile(), а затем подождите 10 мс, после чего повторите попытку. –

0

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

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

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

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