文件目录遍历的并发算法
問題:算出指定目錄下文件的大小.
這個(gè)是個(gè)很簡(jiǎn)單的問題嘛,直接做個(gè)遞歸就行,上順序算法:
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;}}很簡(jiǎn)單,一個(gè)遞歸實(shí)現(xiàn),那么現(xiàn)在我們思考并發(fā)的算法
并發(fā)思路:每次進(jìn)行遞歸運(yùn)算,每次開一個(gè)線程去計(jì)算當(dāng)前目錄的文件大小,然后進(jìn)行遞歸運(yùn)算
并發(fā)代碼:
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>() {@Overridepublic Long call() throws Exception {// TODO Auto-generated method stub//進(jìn)行遞歸,把子目錄的文件大小返回
return getFileSizebf(service, f);}}));}for(final Future<Long> fl : tasks){total += fl.get();}return total;}}}
看上去沒什么問題,我們來實(shí)際測(cè)試下;
我們看到,調(diào)用get從Future中取數(shù)據(jù)的時(shí)候,并沒有設(shè)置超時(shí),實(shí)際運(yùn)行中發(fā)現(xiàn),當(dāng)文件夾的目錄結(jié)構(gòu)簡(jiǎn)單,目錄樹比較淺的時(shí)候能夠跑出來,但是目錄樹結(jié)構(gòu)復(fù)雜了以后,跑了很久還是跑不出來結(jié)果.
分析:我們每個(gè)線程會(huì)開啟新的線程去計(jì)算子目錄的大小,這個(gè)時(shí)候,線程會(huì)進(jìn)入等待,等待子線程的返回結(jié)果,這個(gè)時(shí)候就會(huì)出現(xiàn)一種情況,假如目錄樹的結(jié)構(gòu)復(fù)雜,那么很多線程會(huì)進(jìn)入等待狀態(tài),等待子線程的返回值,但是這些父線程還是占用著線程池,但是子線程請(qǐng)求不到線程池去執(zhí)行,這個(gè)時(shí)候就會(huì)進(jìn)入死鎖.
如下圖:
優(yōu)化策略:1.既然是線程池的poolsize不夠用了,那么我們就增加poolsize的大小嘛,好了,現(xiàn)在我們面對(duì)的是一個(gè)目錄結(jié)構(gòu)不那么復(fù)雜的,通過簡(jiǎn)單地增加poolsize還可以做到,但是非常復(fù)雜的話就沒辦法了.
2.我們之所以會(huì)造成死鎖,是因?yàn)榫€程在占用線程池的情況下同時(shí)在等待子線程的返回結(jié)果,
優(yōu)化版本1:
public class FileOPBX1 implements FileOpI {@Overridepublic long getTotalSizeOfFilesInDir(File f) {long total = 0;final ExecutorService eService = Executors.newFixedThreadPool(100);// TODO Auto-generated method stubfinal List<File> dirs = new ArrayList<>();dirs.add(f);//初始化當(dāng)前文件夾while (!dirs.isEmpty()) {//每次計(jì)算dir里面一集目錄下的文件大小以及子目錄final List<Future<SubDirAndSize>> part = new ArrayList<>();for (final File dir : dirs) {part.add(eService.submit(new Callable<SubDirAndSize>() {@Overridepublic SubDirAndSize call() throws Exception {// TODO Auto-generated method stubreturn getEveryDir(dir);}}));}dirs.clear();//目錄分配任務(wù)完畢,清除//從返回的結(jié)果計(jì)算文件大小,并且把新的子目錄添加到待計(jì)算文件夾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;}//計(jì)算當(dāng)前一級(jí)目錄下文件的大小以及子目錄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);}}}這個(gè)時(shí)候我們看結(jié)果:
運(yùn)行花費(fèi)時(shí)間9688
串行執(zhí)行結(jié)果:19563974028
運(yùn)行花費(fèi)時(shí)間3230
并行執(zhí)行結(jié)果:19563974028
?
轉(zhuǎn)載于:https://www.cnblogs.com/color-my-life/p/4352551.html
總結(jié)
以上是生活随笔為你收集整理的文件目录遍历的并发算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机科学导论学习资料
- 下一篇: (转)FTP的PORT(主动模式)和PA