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

4 线程安全且代码简洁扫描

上面的例子是虽然实现了多线程,但并不简洁。接下来使用BlockingQueue实现扫描操作。BlockingQueue的特点是:如果队列里没有可用空间,则插入操作会被阻塞,若队列里没有可用数据,则删除操作会被阻塞。例子如下:

package gibbon.thread;

import Java.io.File;
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import javax.management.RuntimeErrorException;

public class ConcurrentTotalFileSizeWQueue {
    private ExecutorService service;
    private BlockingQueue<Long> fileSizes = new ArrayBlockingQueue<Long>(500);
    AtomicLong pendingFileVisits = new AtomicLong();
    private void startExploreDir(final File file){
        pendingFileVisits.incrementAndGet(); //共享变量递增
        service.execute(new Runnable() {
           
            @Override
            public void run() {
                // TODO Auto-generated method stub
                exploreDir(file);
            }
        });
    }
   
    private void exploreDir(File file){
        long fileSize = 0;
        if(file.isFile()){
            fileSize = file.length();
        }else{
            File[] children = file.listFiles();
            if(children != null){
                for(File child:children){
                    if(child.isFile()){
                        fileSize += child.length();
                    }else{
                        startExploreDir(child);
                    }
                }
            }
        }
        try {
            fileSizes.put(fileSize);//这个就是阻塞队列的作用,当上面某个线程操作完成进入这一步时,
                                    //线程将被删除,那么线程运行的结果怎么办,于是就在此处完成线程
                                    //间数据交换和同步的操作。这也是为什么说这个例子的代码简洁的原因
        } catch (Exception e) {
            // TODO: handle exception
            throw new RuntimeException(e);
        }
        pendingFileVisits.decrementAndGet();
    }
   
    private long getTotalSizeOfFile(final String fileName)
                    throws InterruptedException{
        long total = 0;
        service = Executors.newFixedThreadPool(100);
        try{
            startExploreDir(new File(fileName));
            while(pendingFileVisits.get()>0 || fileSizes.size()>0){
                Long size = fileSizes.poll(10,TimeUnit.SECONDS);
                total += size;
            }
        }finally{
            service.shutdown();
        }
        return total;
    }
   
    public static void main(String[] args) throws InterruptedException{
        Scanner scanner = new Scanner(System.in);
        String str = scanner.next();
        long start = System.nanoTime();
        long total = new ConcurrentTotalFileSizeWQueue().getTotalSizeOfFile(str);
        long end = System.nanoTime();
        System.out.println("Total Size: " + total);
        System.out.println("Time taken: " + (end-start)/1.0e9);
    }
}

从上面的例子可以看出,使用阻塞队列来并发地解决计算文件的大小问题可以避免死锁问题,每个线程计算所得的部分文件的大小的值插入到一个队列中,而主线程可以遍历该队列获得每部分结果并进行累加。在上面执行队列任务时,当队列满时,便阻塞其他要操作的任务,等待队列中某些任务的完成其他任务才能继续。

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

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