1. 接口CompletionService
接口CompletionService設(shè)計(jì)目標(biāo)在于將生產(chǎn)者和消費(fèi)者解耦,使生產(chǎn)者和消費(fèi)者異步執(zhí)行。
這里所說(shuō)生產(chǎn)者負(fù)責(zé)提交任務(wù)(task)運(yùn)行產(chǎn)生結(jié)果,消費(fèi)者異步獲得運(yùn)行結(jié)果。
該接口定義的一組方法:
// submit提交可執(zhí)行任務(wù),返回Future用于異步獲取結(jié)果
1. Future<V> submit(Callable<V> task);
2. Future<V> submit(Runnable task, V result);
//take返回一個(gè)完成的任務(wù)的結(jié)果,沒(méi)有會(huì)一直阻塞。
3. Future<V> take() throws InterruptedException;
// poll返回一個(gè)完成的任務(wù)的結(jié)果,沒(méi)有返回null
4. Future<V> poll();
// 返回一個(gè)完成的任務(wù)的結(jié)果,沒(méi)有完成的任務(wù)會(huì)阻塞timeout時(shí)間再返回
5. Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
2. 實(shí)現(xiàn)類(lèi)ExecutorCompletionService
ExecutorCompletionService實(shí)現(xiàn)接口CompletionService,下面是ExecutorCompletionService的用法:
public class CompletionServiceTest {
static class RandomSleep implements Callable<Integer>{
private static Random random = new Random(System.currentTimeMillis());
// 實(shí)現(xiàn)Callable, 隨機(jī)sleep n秒,然后返回n。
@Override
public Integer call() throws Exception {
int sleepTime = random.nextInt(20);
System.out.println("sleep " + sleepTime + "s");
Thread.sleep(sleepTime * 1000);
return sleepTime;
}
}
public static void main(String[] args){
// 創(chuàng)建線程池,ExecutorCompletionService本身需要接受傳入的線程池來(lái)運(yùn)行任務(wù)。
ExecutorService cachedThreadPoll = Executors.newCachedThreadPool();
((ThreadPoolExecutor)cachedThreadPoll).setCorePoolSize(5);
// 基于線程池cachedThreadPoll創(chuàng)建ExecutorCompletionService實(shí)例
CompletionService completionService = new ExecutorCompletionService(cachedThreadPoll);
//提交5個(gè)任務(wù)運(yùn)行
for(int i = 0; i < 5; ++i){
completionService.submit(new RandomSleep());
}
cachedThreadPoll.shutdown();
for(int i = 0; i < 5; ++i){
try {
// 上面1提到take會(huì)返回一個(gè)運(yùn)行成功的任務(wù), 否則會(huì)阻塞
System.out.println("get result: " + completionService.take().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
-------------------------------
輸出:
sleep 6s
sleep 8s
sleep 9s
sleep 19s
sleep 10s
get result: 6
get result: 8
get result: 9
get result: 10
get result: 19
從上面的輸出可以看出take按任務(wù)運(yùn)行結(jié)束先后順序返回。
2.1 基本原理
ExecutorCompletionService有三個(gè)成員:
// executor用來(lái)執(zhí)行任務(wù)
private final Executor executor;
// 如果executor實(shí)現(xiàn)了抽象類(lèi)AbstractExecutorService,aes就是executor,否則為null
private final AbstractExecutorService aes;
// 任務(wù)完成后返回的結(jié)果(Future)放入阻塞對(duì)列,take,poll即從這個(gè)對(duì)列取任務(wù)運(yùn)行 結(jié)果
private final BlockingQueue<Future<V>> completionQueue;
每一個(gè)通過(guò)ExecutorCompletionService#submit提交的任務(wù)(實(shí)現(xiàn)Callable或者Runnable)會(huì)被ExecutorCompletionService#newTaskFor包裝成QueueingFuture。
QueueingFuture是ExecutorCompletionService內(nèi)部類(lèi),繼承關(guān)系如下(<<I>>表示接口):
Future<<I>> Runnable<<I>>
^ ^
| |
----------------------
|
RunnableFuture<<I>>
^
|
FutureTask
^
|
QueueingFuture
QueueingFuture繼承類(lèi)FutureTask,F(xiàn)utureTask構(gòu)造函數(shù)包裝了一個(gè)Callable或則Runnable任務(wù)實(shí)例,
FutureTask還有一個(gè)空的protected方法done(),會(huì)在其包裝的任務(wù)運(yùn)行成功、任務(wù)取消或則任務(wù)異常的情況下被調(diào)用。
ExecutorConpletionService內(nèi)部類(lèi)QueueingFuture繼承FutureTask并重寫(xiě)done方法,done方法里將被包裝的task放入阻塞隊(duì)列completionQueue,因此調(diào)用ExecutorCompletionService#take或則poll總是會(huì)獲得運(yùn)行完成的任務(wù)。
注: ExecutorConpletionService#take總是獲得完成的任務(wù),但是這個(gè)任務(wù)可能是正常完成的,也可能是被取消或則拋出了異常。因此還需要對(duì)take返回的Future作出判斷再進(jìn)一步處理。