Java併發編程的藝術(九)——批量獲取多條線程的執行結果

當向線程池提交callable任務後,我們可能需要一次性獲取所有返回結果,有三種處理方法。

方法一:自己維護返回結果

// 創建一個線程池
ExecutorService executorService = Executors.newFixedThreadPool(10);

// 存儲執行結果的List
List<Future<String>> results = new ArrayList<Future<String>>();

// 提交10個任務
for ( int i=0; i<10; i++ ) {
    final int tmp = i;
    Future<String> result = executorService.submit( new Callable<String>(){
        public String call(){
            int sleepTime = new Random().nextInt(1000);
            Thread.sleep(sleepTime);
            return "線程"+tmp+"睡了"+sleepTime+"秒";
        }
    } );
    // 將執行結果存入results中
    results.add( result );
}

// 獲取10個任務的返回結果
for ( int i=0; i<10; i++ ) {
    // 獲取包含返回結果的future對象
    Future<String> future = results.get(i);
    // 從future中取出執行結果(若尚未返回結果,則get方法被阻塞,直到結果被返回爲止)
    String result = future.get();
    System.out.println(result);
}

此方法的弊端:

  1. 需要自己創建容器維護所有的返回結果,比較麻煩;
  2. 從list中遍歷的每個Future對象並不一定處於完成狀態,這時調用get()方法就會被阻塞住,如果系統是設計成每個線程完成後就能根據其結果繼續做後面的事,這樣對於處於list後面的但是先完成的線程就會增加了額外的等待時間。

方法二:使用ExecutorService的invokeAll函數

本方法能解決第一個弊端,即並不需要自己去維護一個存儲返回結果的容器。當我們需要獲取線程池所有的返回結果時,只需調用invokeAll函數即可。
但是,這種方式需要你自己去維護一個用於存儲任務的容器。

// 創建一個線程池
ExecutorService executorService = Executors.newFixedThreadPool(10);

// 創建存儲任務的容器
List<Callable<String>> tasks = new ArrayList<Callable<String>>();

// 提交10個任務
for ( int i=0; i<10; i++ ) {
    Callable<String> task = new Callable<String>(){
        public String call(){
            int sleepTime = new Random().nextInt(1000);
            Thread.sleep(sleepTime);
            return "線程"+i+"睡了"+sleepTime+"秒";
        }
    };
    executorService.submit( task );
    // 將task添加進任務隊列
    tasks.add( task );
}

// 獲取10個任務的返回結果
List<Future<String>> results = executorService.invokeAll( tasks );

// 輸出結果
for ( int i=0; i<10; i++ ) {
    // 獲取包含返回結果的future對象
    Future<String> future = results.get(i);
    // 從future中取出執行結果(若尚未返回結果,則get方法被阻塞,直到結果被返回爲止)
    String result = future.get();
    System.out.println(result);
}

方法三:使用CompletionService

CompletionService內部維護了一個阻塞隊列,只有執行完成的任務結果纔會被放入該隊列,這樣就確保執行時間較短的任務率先被存入阻塞隊列中。

ExecutorService exec = Executors.newFixedThreadPool(10);

final BlockingQueue<Future<String>> queue = new LinkedBlockingDeque<Future<String>>(  
                10);  
        //實例化CompletionService  
        final CompletionService<String> completionService = new ExecutorCompletionService<String>(  
                exec, queue); 

// 提交10個任務
for ( int i=0; i<10; i++ ) {
    final int tmp = i;
completionService.submit( new Callable<String>(){
public String call(){ int sleepTime = new Random().nextInt(1000); Thread.sleep(sleepTime); return "線程"+tmp+"睡了"+sleepTime+"秒"; } } );}
// 輸出結果
for ( int i=0; i<10; i++ ) {
    // 獲取包含返回結果的future對象(若整個阻塞隊列中還沒有一條線程返回結果,那麼調用take將會被阻塞,當然你可以調用poll,不會被阻塞,若沒有結果會返回null,poll和take返回正確的結果後會將該結果從隊列中刪除)
    Future<String> future = completionService.take();
    // 從future中取出執行結果,這裏存儲的future已經擁有執行結果,get不會被阻塞
    String result = future.get();
    System.out.println(result);
}



轉自:http://blog.csdn.net/u010425776/article/details/54580710

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章