問題:算出指定目錄下文件的大小.算法
這個是個很簡單的問題嘛,直接作個遞歸就行,上順序算法:多線程
public long getFileSize(final File file){ if(file.isFile()){ return file.length(); } else{ long total = 0; File files[] = file.listFiles(); if(files == null)return 0; for(File f: files){ total += getFileSize(f); } return total; } }
很簡單,一個遞歸實現,那麼如今咱們思考併發的算法併發
併發思路:每次進行遞歸運算,每次開一個線程去計算當前目錄的文件大小,而後進行遞歸運算ide
併發代碼:測試
private long getFileSizebf(final ExecutorService service,final File file) throws InterruptedException, ExecutionException, TimeoutException{ if(file.isFile()) return file.length(); else{ final File files[] = file.listFiles(); if(files == null) return 0; else{ long total = 0; final List<Future<Long>> tasks = new ArrayList<Future<Long>>(); for(final File f : files){ tasks.add(service.submit(new Callable<Long>() { @Override public Long call() throws Exception { // TODO Auto-generated method stub
//進行遞歸,把子目錄的文件大小返回
return getFileSizebf(service, f); } })); } for(final Future<Long> fl : tasks){ total += fl.get(); } return total; } } }
看上去沒什麼問題,咱們來實際測試下;優化
咱們看到,調用get從Future中取數據的時候,並無設置超時,實際運行中發現,當文件夾的目錄結構簡單,目錄樹比較淺的時候可以跑出來,可是目錄樹結構複雜了之後,跑了好久仍是跑不出來結果.spa
分析:咱們每一個線程會開啓新的線程去計算子目錄的大小,這個時候,線程會進入等待,等待子線程的返回結果,這個時候就會出現一種狀況,假如目錄樹的結構複雜,那麼不少線程會進入等待狀態,等待子線程的返回值,可是這些父線程仍是佔用着線程池,可是子線程請求不到線程池去執行,這個時候就會進入死鎖.線程
以下圖:code
優化策略:1.既然是線程池的poolsize不夠用了,那麼咱們就增長poolsize的大小嘛,好了,如今咱們面對的是一個目錄結構不那麼複雜的,經過簡單地增長poolsize還能夠作到,可是很是複雜的話就沒辦法了.blog
2.咱們之因此會形成死鎖,是由於線程在佔用線程池的狀況下同時在等待子線程的返回結果,
優化版本1:
public class FileOPBX1 implements FileOpI { @Override public long getTotalSizeOfFilesInDir(File f) { long total = 0; final ExecutorService eService = Executors.newFixedThreadPool(100); // TODO Auto-generated method stub final List<File> dirs = new ArrayList<>(); dirs.add(f);//初始化當前文件夾 while (!dirs.isEmpty()) { //每次計算dir裏面一集目錄下的文件大小以及子目錄 final List<Future<SubDirAndSize>> part = new ArrayList<>(); for (final File dir : dirs) { part.add(eService.submit(new Callable<SubDirAndSize>() { @Override public SubDirAndSize call() throws Exception { // TODO Auto-generated method stub return getEveryDir(dir); } })); } dirs.clear();//目錄分配任務完畢,清除 //從返回的結果計算文件大小,而且把新的子目錄添加到待計算文件夾 for (final Future<SubDirAndSize> subdir : part) { try { final SubDirAndSize sas = subdir.get(); total += sas.size; dirs.addAll(sas.subDirs); } catch (InterruptedException | ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(dirs); } return total; } //計算當前一級目錄下文件的大小以及子目錄 private SubDirAndSize getEveryDir(final File f) { long total = 0; final List<File> subDirs = new LinkedList<File>(); if (f.isDirectory()) { final File[] sub = f.listFiles(); if (sub != null) { for (final File dir : sub) { if (dir.isFile()) total += dir.length(); else { subDirs.add(dir); } } } } return new SubDirAndSize(total, subDirs); } public class SubDirAndSize { final public long size; final public List<File> subDirs; public SubDirAndSize(final long totalsize, final List<File> thesubDir) { size = totalsize; subDirs = Collections.unmodifiableList(thesubDir); } } }
這個時候咱們看結果:
運行花費時間9688串行執行結果:19563974028運行花費時間3230並行執行結果:19563974028