Java利用多线程计算目录数据大小(2)

3 线程安全但不简洁扫描

因此,通过前面的分析,我们知道这里死锁的原因是因为子目录多而占用了线程,所以改进一种办法,这就是在扫描目录的子目录和文件的时候,把该目录下的子目录列表和所有文件的大小都返回给主线程。同样,先看例子再进行解释:

package gibbon.thread;

import Java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ConcurrentTotalFileSize {
    class SubDirectoriesAndSize{
        final public long size;
        final public List<File> subDirectories;
        public SubDirectoriesAndSize(final long totalSize,final List<File> theSubDirs){
            size = totalSize;
            subDirectories = theSubDirs;
        }
    }
   
    private SubDirectoriesAndSize getTotalAndSubDirs(final File file){
        long total = 0;
        final List<File> subDirectories = new ArrayList<File>();
        //检查文件是不是一个目录
        if(file.isDirectory()){
            //获得该文件的子文件
            final File[] children = file.listFiles();
            if(children != null){
                for(final File child:children){
                    System.out.println("get the child file: " + child.getPath().toString());
                    if(child.isFile()){
                        total += child.length();
                    }else{
                        subDirectories.add(child);
                    }
                }
            }
        }
        return new SubDirectoriesAndSize(total, subDirectories);
    }
   
    private long getTotalSizeOfFileInDir(final File file)
            throws InterruptedException,ExecutionException,TimeoutException{
        long total = 0;
        long count = 0;
        final ExecutorService service = Executors.newFixedThreadPool(100);
        try {
           
            final List<File> directories = new ArrayList<File>();
            directories.add(file);
            for(int i=0;i<directories.size();i++){
                System.out.println("get the file is: " + directories.get(i).toString());
            }
            while(!directories.isEmpty()){
                final List<Future<SubDirectoriesAndSize>> partialResults =
                        new ArrayList<Future<SubDirectoriesAndSize>>();
                for(final File directory:directories){
                    System.out.println("file is " + directory.toPath().toString());
                    partialResults.add(
                            service.submit(new Callable<SubDirectoriesAndSize>() {
                        public SubDirectoriesAndSize call(){
                            return getTotalAndSubDirs(directory);
                        }
                    }));
                }
                directories.clear();
                for(final Future<SubDirectoriesAndSize> partialResultFuture:partialResults){
                    final SubDirectoriesAndSize subDirectoriesAndSize =
                            partialResultFuture.get(100, TimeUnit.SECONDS);//这里设置的时间和线程池的大小是有关系的
                    directories.addAll(subDirectoriesAndSize.subDirectories);
                    total += subDirectoriesAndSize.size;
                }
            }
           
        } catch (Exception e) {
            // TODO: handle exception
            service.shutdown();
        }
        return total;
    }
   
    public static void main(String[] args)
            throws InterruptedException,ExecutionException,TimeoutException{
        Scanner scanner = new Scanner(System.in);
        String str = scanner.next();
        final long start = System.nanoTime();
        final long total = new ConcurrentTotalFileSize().getTotalSizeOfFileInDir(new File(str));
        final long end = System.nanoTime();
        System.out.println("Total Size: " + total);
        System.out.println("Time taken: " + (end-start)/1.0e9);
    }
}

在上面的例子中,当输入最顶层目录之后,只要遇到还有待扫描的目录,那么就在单独的线程中调用getTotalAndSubDirs()为每个目录执行计算任务。当所有线程的响应返回之后,就可以得到文件的大小的部分和累加,并把子目录列表放进待扫描队列中,当所有的子目录扫描完毕之后,就能得到整个目录的大小。于是上面的线程执行限时可以不加。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/da4950327326c33eb9dc566f28861d71.html