1. 程式人生 > >Java併發程式設計的藝術(九)——批量獲取多條執行緒的執行結果

Java併發程式設計的藝術(九)——批量獲取多條執行緒的執行結果

這裡寫圖片描述

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

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

// 建立一個執行緒池
ExecutorService executorService = Executors.newFixedThreadPool(10);

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

// 提交10個任務
for ( int i=0; i<10; i++ ) {
    Future<String> result = executorService.submit( new
Callable<String>(){ public String call(){ int sleepTime = new Random().nextInt(1000); Thread.sleep(sleepTime); return "執行緒"+i+"睡了"+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<Integer>> queue = new LinkedBlockingDeque<Future<Integer>>(  
                10);  
        //例項化CompletionService  
        final CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(  
                exec, queue); 

// 提交10個任務
for ( int i=0; i<10; i++ ) {
    executorService.submit( new Callable<String>(){
        public String call(){
            int sleepTime = new Random().nextInt(1000);
            Thread.sleep(sleepTime);
            return "執行緒"+i+"睡了"+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);
}

這裡寫圖片描述