1

我已经构建了一个简单的测试,它在无限循环中创建和删除一个文件(名称不会改变)。测试确实运行了几秒钟(有时超过 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;
}

谁能解释这种行为?

4

3 回答 3

2

我在写问题时找到了解释。我仍然发布了这个问题,因为我想分享我学到的东西。

我的应用程序不是系统上访问文件的唯一进程。例如,Windows 搜索索引服务可以打开这个文件,因为它想将它添加到它的索引中。或者 Windows Explorer(如果它正在更新视图)。

于 2012-05-09T12:53:50.613 回答
0

这个问题让我想起了我最近在使用 File.renameTo() 方法时遇到的一个问题。这是(是?)由于 jvm 中的这个错误:

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

一个奇怪的解决方法是调用 System.gc() 并再次重命名文件(它可以工作......)。

不确定它是否与您的问题有关,但可能值得探索......

于 2012-05-09T13:41:58.460 回答
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 之前通过 delete 释放文件。

于 2012-05-09T13:07:24.697 回答