首页 > 技术文章 > 文件目录遍历的并发算法

color-my-life 2015-03-20 02:02 原文

问题:算出指定目录下文件的大小.

这个是个很简单的问题嘛,直接做个递归就行,上顺序算法:

    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

 

推荐阅读