文件目錄遍歷的並發算法


問題:算出指定目錄下文件的大小.

這個是個很簡單的問題嘛,直接做個遞歸就行,上順序算法:

    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;
        }
    }

很簡單,一個遞歸實現,那么現在我們思考並發的算法

並發思路:每次進行遞歸運算,每次開一個線程去計算當前目錄的文件大小,然后進行遞歸運算

並發代碼:

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中取數據的時候,並沒有設置超時,實際運行中發現,當文件夾的目錄結構簡單,目錄樹比較淺的時候能夠跑出來,但是目錄樹結構復雜了以后,跑了很久還是跑不出來結果.

分析:我們每個線程會開啟新的線程去計算子目錄的大小,這個時候,線程會進入等待,等待子線程的返回結果,這個時候就會出現一種情況,假如目錄樹的結構復雜,那么很多線程會進入等待狀態,等待子線程的返回值,但是這些父線程還是占用着線程池,但是子線程請求不到線程池去執行,這個時候就會進入死鎖.

如下圖:

優化策略:1.既然是線程池的poolsize不夠用了,那么我們就增加poolsize的大小嘛,好了,現在我們面對的是一個目錄結構不那么復雜的,通過簡單地增加poolsize還可以做到,但是非常復雜的話就沒辦法了.

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM