1 順序掃描
提到計算目錄數據大小,我們首先想到的會是順序遍歷每個文件,並累加遍歷後的結果。如下面例子,該例子使用順序計算目錄大小的方法。
public class TotalFileSizeSequential{
private long getTotalSizeOfFileInDir(final File file){
if(file.isFile()){
return file.length();
}
final File[] children = file.listFile();
if(children!=null){
for(final File child:children){
total += getTotalSizeOfFileInDir(child);//遞歸遍歷
}
}
return total
}
public void static main(String[] args){
Scanner scanner = new Scanner(System.in);
String str = scanner.next(); //輸入目錄
final long start = System.nanoTime();
final long total = new TotalFileSizeSequential().getTotalSizeOfFilesInDir(new File(str));
final long end = System.nanoTime();
System.out.println("文件總大小 " + total);
System.out.println("所用時間 " + (end-start)/1.0e9);
}
}
2 線程不安全掃描
從上面例子可以看出,這裡並為使用了多線程來進行計算,而是按照執行順序來累加結果。雖然結果是正確的,但卻不是高效的。所以接下來我們使用多線程的一個例子,但該例子遇到了一個問題,那就是線程死鎖問題,先看下再進行具體分析:
public class NavelyConcurrentTotalFileSize{
private long getTotalSizeOfFileInDir(ExecutorService service,File file)
throws InterruptedException,ExecutionException,TimeoutException{
if(file.isFile()){
return file.length();
}
long total = 0;
final File[] children = file.listFile();
if(children != null){
final List<Future<Long>> partialTotalFuture = new ArrayList<Future<Long>>();
for(final File child:children){
partialTotalFuture.add(service.submit(new Callable<Long>{ //執行任務
public Long call() throws InterruptedException,
ExecutionException,TimeoutException{
return getTotalSizeOfFilesInDir(service,child);
}
}));
}
for(Future<Long> partialTotalFuture:partialTotalFutures){
total += partialTotalFuture.get(100,TimeUnit.SECODES);//計算結果
}
}
return tatal;
}
private long getTotalSizeOfFile(string fileName) throws InterruptedException,
ExecutionException,TimeoutException{
ExecutorService service = Executors.newFixedThreadPool(100);
try{
return getTotalSizeOfFileInDir(service,new File(fileName));
}finally{
service.shutdown();
}
}
public void static main(String[] args){
Scanner scanner = new Scanner(System.in);
String str = scanner.next(); //輸入目錄
final long start = System.nanoTime();
final long total = new NaivelyConcurrentTotalFileSize().getTotalSizeOfFile(str);
final long end = System.nanoTime();
System.out.println("文件總大小 " + total);
System.out.println("所用時間 " + (end-start)/1.0e9);
}
}
上面的這個例子開啟了一個100大小的線程池,並在Future執行結果的時候設定了運行時間,超過運行時間將拋出異常。這裡,當我們的目錄沒那麼多文件的時候,並不存在問題,但當目錄包含很大文件的時候,此時每當遇到一個子目錄,就通過service.submit執行任務,既把改任務調度給其他線程,照如此下去,當我們還未深入到最底層目錄時,由於線程數目的限制,導致線程池內的等待某些任務的相應,而這些任務卻在ExcotorService的隊列中等待執行的機會,因為線程是遞歸的,即從最外層到最裡層,所以在等待最裡層返回結果,但是最裡層又沒有額外的線程來執行,於是形成死鎖狀態。最後因為設置了超時,避免程序處於假死狀態。