對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!");
    }
這樣,問題就解決了。

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