2011-03-04 139 views
7

在遇到與mkdirs()有關的問題並在互聯網上徘徊後,我得到mkdirs()存在線程安全問題的印象。如何在Java中以線程安全的方式使用mkdirs?

有可能確保在多個線程可能嘗試創建類似的文件結構時可以正確創建目錄嗎?

感謝

(在我的情況下,我會在Android上使用本)

+0

你能不能提供,爲什麼你想引用'mkdirs()'是不是線程安全的? – 2011-03-04 08:33:29

+0

是否有任何細節已知(適用於J2SE和Android)在哪種情況下,此問題很可能出現?CPU內核,線程,操作系統,文件系統的數量?我試圖爲測試目的提出這個問題。 – Robert 2011-07-04 09:14:05

+0

@TedHopp鏈接已死?你能重新鏈接或解釋它是什麼嗎? – 2014-11-07 10:06:30

回答

0

好吧,我知道這已經停用了一段時間,但我想也許有一個簡單的解決方案。您在該問題的評論中鏈接的文章似乎表明,唯一的問題是目錄而不是正在創建。解決方案是這樣做的:

if (!f.mkdirs()) { 
    f.mkdirs(); 
} 

但是,這似乎效率低下,仍然可能有問題。那麼,爲什麼不簡單地這樣做:

while (!f.mkdirs()) {} 

很簡單,但它的工作原理。

編輯:思考了一下之後,這個例子可能滯後於遺忘,並可能導致線程鎖定。所以,這可能是一個更好的主意:

while (!f.mkdirs()) { Thread.yield(); } 

當然,如果你在這可能導致螺紋鎖,只要它不是一個高優先級的情況下一個線程是那個只會建議。把它放在那裏。

+0

...真的,真的希望這種情況不會在mkdirs()因爲真正的原因而失敗的情況下運行,例如權限問題或格式錯誤的目錄名稱。 – 2012-10-01 15:17:18

+0

@smackfu好點。也許最好嘗試一定次數,然後顯示錯誤消息。 – 2012-10-01 20:19:39

+0

我有確切的問題,遺憾的是沒有上述對我的工作,但這個工作',而(!dir.mkdirs()) \t \t {\t \t \t \t如果(dir.isDirectory()) \t \t \t { \t \t \t \t break; \t \t \t} \t \t \t \t \t}' – 2014-11-07 12:03:08

2

一個可能的解決方案將是一個MkDirService(如下圖所示),保證只有一個實例,並在它自己的線程中運行。利用BlockingQueue。

首先服務:

package mkdir; 

import java.io.File; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.LinkedBlockingQueue; 

public class MkDirService extends Thread { 

    private static MkDirService service; 
    private BlockingQueue<File> pendingDirs = new LinkedBlockingQueue<File>(); 
    private boolean run = true; 

    private MkDirService() { 
    } 

    public synchronized static MkDirService getService() { 
     if (service == null) { 
      service = new MkDirService(); 
      new Thread(service).start(); 
     } 
     return service; 
    } 

    public void makeDir(File dir) { 
     pendingDirs.add(dir); 
    } 

    public void shutdown() { 
     run = false; 
    } 

    @Override 
    public void run() { 
     while (run || !pendingDirs.isEmpty()) { 
      File curDir = null; 
      try { 
       curDir = pendingDirs.take(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      if (curDir != null && !curDir.exists()) { 
       curDir.mkdir(); 
       System.out.println("Made: " + curDir.getAbsolutePath()); 
      } 
     } 
    } 
} 

的測試:

package mkdir; 

import java.io.File; 

public class MkDirServiceTest { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     MkDirService mdServ = MkDirService.getService(); 
     mdServ.makeDir(new File("test1")); 
     mdServ.makeDir(new File("test1/test2")); 
     mdServ.makeDir(new File("test1/test3")); 
     mdServ.shutdown(); 

    } 
} 
+1

我會歡迎任何對我的例子的更正,我相信有一些更好的方法來做到這一點,並會認爲這是一個很好的學習機會。 – bconneen 2011-03-04 05:04:59

3

不要在序列化的一切工作線程所有目錄的創建。您可以使用LooperHandler來簡化將調用mkdirs的Runnables發佈到工作線程。完成目錄製作後,可以調用Looper.quit()在處理最後發佈的Runnable之後結束該線程。該documentation for Looper有示例代碼,顯示這是多麼接近微不足道。

5

我不知道,如果Android支持的併發包,但這裏是我的看法:

private static Lock fsLock = new ReentrantLock(); 

private void mkdir(File dir) throws FileNotFoundException { 

    if(dir.exists()) { 
     return; 
    } 

    fsLock.lock(); 
    try { 
     if(!dir.exists()) { 
      log.info("Creating directory {}", dir.getAbsolutePath()); 
      if(!dir.mkdirs()) { 
       throw new FileNotFoundException("Can't create directory " + dir.getAbsolutePath()); 
      } 
     } 
    } finally { 
     fsLock.unlock(); 
    } 
} 

的方法早期如果該目錄已經存在返回。如果它不存在,只有一個線程會嘗試創建它。

+0

這太好了。這解決了我與多線程嘗試創建相同目錄的問題。不是在Android上,而是在常規的Java 5應用程序中。 – Melloware 2011-07-19 15:54:14

+0

是的,這比看起來更難。 – 2011-07-19 16:28:54

1

Eaven如果該線程是一個年紀大一點我不知道是否有somethink錯了以下解決方案:

package service; 

import java.io.File; 

public class FileService { 

    public static synchronized boolean mkdirs(File dir) { 
     return dir.mkdirs(); 
    } 
} 
相關問題