歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java利用多線程計算目錄數據大小

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的隊列中等待執行的機會,因為線程是遞歸的,即從最外層到最裡層,所以在等待最裡層返回結果,但是最裡層又沒有額外的線程來執行,於是形成死鎖狀態。最後因為設置了超時,避免程序處於假死狀態。

Copyright © Linux教程網 All Rights Reserved