zoukankan      html  css  js  c++  java
  • 文件目录遍历的并发算法

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

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

        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

  • 相关阅读:
    购买 Linux VPS 服务器后简单的安全设置
    VPS性能测试:CPU内存,硬盘IO读写,带宽速度,UnixBench和压力测试
    Polysh实现多服务器批量执行shell
    第十一周编程总结
    第十周作业
    第九周编程总结修改
    第九周编程总结
    第八周编程总结
    第7周编程总结
    第七周编程总结啊
  • 原文地址:https://www.cnblogs.com/color-my-life/p/4352551.html
Copyright © 2011-2022 走看看