版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/qq_34173549/article/details/79612353
當向線程池送出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
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
此方法的弊端:
- 需要自己建立容器維護所有的傳回結果,比較麻煩;
- 從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);
}
- 28
- 29
- 30
- 31
方法三:使用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);
}