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()为每个目录执行计算任务。当所有线程的响应返回之后,就可以得到文件的大小的部分和累加,并把子目录列表放进待扫描队列中,当所有的子目录扫描完毕之后,就能得到整个目录的大小。于是上面的线程执行限时可以不加。