java多線程(8):Callable、FutureTask的應用

簡介

  • Callable

Callable是相似Runnable的類,主要區別在於Callable是能夠返回結果,而Runnable不會。java

  • FutureTask

簡單說FutureTask的做用是能夠啓用、取消、而且判斷線程是否完成,能夠搭配Callable和Runnable使用。和Callable一塊兒使用時,能夠實如今線程完成任務後獲取返回結果。git

應用

衆所周知,爲何要使用多線程,無非是爲了加快任務的處理速度。好比如今須要你計算某個目錄下的文件數量,你能夠選擇單線程遍歷統計,也能夠考慮使用多線程遍歷查詢。github

public class FileCountCallableTest {

    public static void main(String[] args){
        //填寫你想統計的文件夾目錄
        File root = new File("D:\\");
        countByConcurrent(root);
        countBySingle(root);
    }

    /**
     * 多線程計算文件數量
     *
     * 獲取目標目錄下的子目錄,並把每一個子目錄生成新線程任務達到加快計算目的
     *
     * @param targetDir
     */
    static void countByConcurrent(File targetDir){
        try {
            long t1 = System.currentTimeMillis();
            File[] files = targetDir.listFiles();
            ExecutorService es = Executors.newFixedThreadPool(4);
            List<FutureTask> futureTaskList = new ArrayList<FutureTask>();
            int sumCount = 0;
            for(File file:files){
                if(file.isDirectory()){
                    //每一個目錄生成新線程任務
                    FileCountCallable fileCountCallable = new FileCountCallable(file.getPath());
                    FutureTask<Integer> futureTask = new FutureTask(fileCountCallable);
                    es.submit(futureTask);
                    futureTaskList.add(futureTask);
                }else{
                    sumCount++;
                }
            }
            //把每一個任務的計算結果相加,得出最終結果
            for(FutureTask<Integer> futureTask:futureTaskList){
                sumCount += futureTask.get();
            }
            es.shutdown();
            System.out.println("sumCount:"+sumCount);
            System.out.println("countByConcurrent finish takes:"+(System.currentTimeMillis() - t1));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 單線程計算文件數量
     *
     * @param targetDir
     */
    static void countBySingle(File targetDir){
        try {
            long t1 = System.currentTimeMillis();
            int sumCount = FileHelper.searchFiles(targetDir);
            System.out.println("sumCount:"+sumCount);
            System.out.println("countBySingle finish takes:"+(System.currentTimeMillis() - t1));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class FileCountCallable implements Callable<Integer>{

    private File root;

    public Integer call() {
        long t1 = System.currentTimeMillis();
        int count = FileHelper.searchFiles(root);
        return count;
    }

    public FileCountCallable(String pathName) {
        root = new File(pathName);
    }
}

代碼比較簡單,好比一個目錄下有x、y、z三個文件夾,我能夠一個線程去遍歷計算,也能夠啓用3個線程分別去計算,最終把3個線程的結果相加就是最終結果。
QQ截圖20191011211755.png
最終我在本機上測試的結果:sql

sumCount:155963
countByConcurrent finish takes:7793 ms
sumCount:155963
countBySingle finish takes:9608 ms

採用多線程任務的方式,節省了18.9%的時間,相差並非很大,主要是由於個人文件分佈不均勻,採起以上策略時有的任務工做量大、有的很小,若是是分佈比較均勻的話節省時間能夠達到50%。固然要根據實際的需求調整多任務的策略,儘可能使每一個任務的工做量相差並不大。數據庫

還有在咱們實際項目中可使用的場景:api

  • http請求:好比某個功能,須要你去5個api去獲取數據。
  • 文件處理: 好比圖片壓縮、水印
  • 數據庫:多個sql查詢

最後祝你們早日發財多線程

源碼地址測試

相關文章
相關標籤/搜索