对java中File.mkdirs方法线程安全问题的一点探讨

mkdirs是java中用来一次创建多级目录的方法,在java.io.File类中。

 

我在编程中遇到这样一段代码:
    if (!dest.exists()) {
        dest.mkdirs();
    }
    if (!dest.isDirectory()) {
      throw new IOException(dest.getName() + " must be a directory!");
    }

 

该段代码所在的方法会被多个线程调用。在某些次的运行中,dest.mkdirs方法创建目录就会失败,从而造成下面的if语句里的抛异常代码被执行。

 

JDK 1.5 update 21中的java.io.File.mkdirs代码如下:
    public boolean mkdirs() {
         if (exists()) {
             return false;
         }
         if (mkdir()) {
            return true;
        }
        File canonFile = null;
        try {
            canonFile = getCanonicalFile();
        } catch (IOException e) {
            return false;
        }
         String parent = canonFile.getParent();
        return (parent != null) &&
               (new File(parent, fs.prefixLength(parent)).mkdirs() &&
                                    canonFile.mkdir());
    }

 

考察该mkdirs源码,发现在这种可能的情形中,会出问题:两个线程都调用最前面那段代码,第一个线程中要创建的目录为“dirParent/dirA”,第二个线程要创建的目录为“dirParent/dirB”(执行以前dirParent目录不存在)。这两个线程都进入mkdirs方法,两个线程都没能直接创建目录并执行到“return”语句那一行,接下来,第一个线程执行完毕并成功创建了“dirParent/dirA”目录,这时第二个线程向前推进,对mkdirs方法的递归调用由于“dirParent”目录已存在而返回false,由“&&”运算符的短路机制,“canonFile.mkdir()”语句将不会被执行,也就是说“dirB”目录并没有被创建出来,且此时mkdirs也将返回false。于是,在第二个线程中,后面的抛出异常的语句就被执行。

 

这样看来,mkdirs方法并不是线程安全的。针对这段程序的实际情况,我将最上面的代码改成下面这样(FileUtils是该段代码所在的类,且该段代码所在的方法为静态方法):
    if (!dest.exists()) {
        synchronized (FileUtils.class) {
            dest.mkdirs();
        }
    }
    if (!dest.isDirectory()) {
      throw new IOException(dest.getName() + " must be a directory!");
    }
这样,问题就解决了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章