JDK并發(fā)包 - ExecutorCompletionService

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)一步處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,837評(píng)論 18 139
  • 譯序 本指南根據(jù) Jakob Jenkov 最新博客翻譯,請(qǐng)隨時(shí)關(guān)注博客更新:http://tutorials.j...
    高廣超閱讀 5,158評(píng)論 1 68
  • 一、并發(fā) 進(jìn)程:每個(gè)進(jìn)程都擁有自己的一套變量 線程:線程之間共享數(shù)據(jù) 1.線程 Java中為多線程任務(wù)提供了很多的...
    SeanMa閱讀 2,522評(píng)論 0 11
  • 作為一部極具票房號(hào)召力的影片,加勒比海盜導(dǎo)演及幕后確實(shí)下了一番功夫去為極為簡(jiǎn)單的劇情添枝加葉,包括各種特效和大開(kāi)的...
    Liligan閱讀 511評(píng)論 0 3
  • 我有一些例子, 女孩A,她學(xué)習(xí)很好,下面還有一個(gè)妹妹,她的爸爸從來(lái)不顧家也不著家,對(duì)她們兩姐妹也很少關(guān)心,她上學(xué)的...
    Dankyou閱讀 272評(píng)論 1 1