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