12.线程池-5

CompletionService介绍

CompletionService

通过上面的截图,我们大概可以猜到CompletionService跟线程池的队列有关。java.util.concurrent.CompletionService 是对 ExecutorService 的一个功能增强封装,优化了获取异步操作结果的接口。CompletionService接口功能是以异步的方式将执行任务和处理任务分别执行,避免阻塞。
在上一节介绍execute和submit的区别的时候有提到submit有返回值,可以返回Future。当我们执行Future的get方法获取结果时,可能拿到的Future并不是第一个执行完成的Callable的Future,就会进行阻塞,可能会导致严重的性能损耗问题。而CompletionService正是为了解决这个问题,它是Java8的新增接口,它的实现类是ExecutorCompletionService。CompletionService会根据线程池中Task的执行结果按执行完成的先后顺序排序,任务先完成的可优先获取到。下面通过代码来分别对别下:
直接get获取:

public class CompletionServiceTest {

    private static final  int PROCESS_SIZE = Runtime.getRuntime().availableProcessors();

    private static final  int CORE_POOL_SIZE = PROCESS_SIZE;

    private static final  int KEEP_ALIVE_TIME = 5;

    private static final  int WORK_QUEUE_SIZE = PROCESS_SIZE;
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
                PROCESS_SIZE * 2,
                KEEP_ALIVE_TIME, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(WORK_QUEUE_SIZE));

        List<Future<String>> futureList = new ArrayList<>();
        
        // 两个任务,分别休眠4s,1s
        futureList.add(threadPoolExecutor.submit(new MyCallable(4_000)));
        futureList.add(threadPoolExecutor.submit(new MyCallable(1_000)));

        for (Future future : futureList) {
            try {
                // get方法是阻塞的,如果线程池中某一个任务执行较长,会阻塞其他已经完成的任务获取结果.
                System.out.println("get方法阻塞:" + future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        threadPoolExecutor.shutdown();
    }

    /**
     * call():休眠sleepTime,任务开始以及结束打印下内容.
     * 
     */
     static class MyCallable implements Callable<String> {

        private int sleepTime;
        public MyCallable(int sleepTime) {
            this.sleepTime = sleepTime;
        }

        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName() + "  start,sleepTime:" + sleepTime);
            Thread.sleep(sleepTime);
            return "end.当前线程名字:" + Thread.currentThread().getName() + ",休眠了:" + sleepTime;
        }
    }
}
**************************************************************
pool-1-thread-2  start,sleepTime:1000
pool-1-thread-1  start,sleepTime:4000
get方法阻塞:end.当前线程名字:pool-1-thread-1,休眠了:4000
get方法阻塞:end.当前线程名字:pool-1-thread-2,休眠了:1000

使用CompletionService:

public class CompletionServiceTest {

    private static final int PROCESS_SIZE = Runtime.getRuntime().availableProcessors();

    private static final int CORE_POOL_SIZE = PROCESS_SIZE;

    private static final int KEEP_ALIVE_TIME = 5;

    private static final int WORK_QUEUE_SIZE = PROCESS_SIZE;

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
                PROCESS_SIZE * 2,
                KEEP_ALIVE_TIME, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(WORK_QUEUE_SIZE));

        CompletionService completionService = new ExecutorCompletionService(threadPoolExecutor);
        //提交Callable任务
        completionService.submit(new MyCallable(4_000));
        completionService.submit(new MyCallable(1_000));
        //获取future结果,不会阻塞
        Future<String> pollFuture = completionService.poll();
        //call方法中会休眠1s,这里因为没有执行完成的Callable,所以返回null
        System.out.println("没有执行完毕:" + pollFuture);


        //获取future结果,最多等待1秒,不会阻塞
     /*   Future<Integer> pollTimeOutFuture = completionService.poll(1, TimeUnit.SECONDS);*/

        //通过take获取Future结果,此方法会阻塞
        for (int i = 0; i < 2; i++) {
            System.out.println("--------" + completionService.take().get());
        }

        threadPoolExecutor.shutdown();
    }

    /**
     * call():休眠sleepTime,任务结束打印结果.
     */
    static class MyCallable implements Callable<String> {

        private int sleepTime;

        public MyCallable(int sleepTime) {
            this.sleepTime = sleepTime;
        }

        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName() + "  start,sleepTime:" + sleepTime);
            Thread.sleep(sleepTime);
            return "end.当前线程名字:" + Thread.currentThread().getName() + ",休眠了:" + sleepTime;
        }
    }
}
*********************************************************
没有执行完毕:null
pool-1-thread-1  start,sleepTime:4000
pool-1-thread-2  start,sleepTime:1000
--------end.当前线程名字:pool-1-thread-2,休眠了:1000
--------end.当前线程名字:pool-1-thread-1,休眠了:4000

上面代码可以清晰看出使用CompletionService优点,CompletionService没有采取依次遍历 Future 的方式,而是在中间加上了一个结果队列,任务完成后马上将结果放入队列,那么从队列中取到的就是最早完成的结果。如果队列为空,那么 take() 方法会阻塞直到队列中出现结果为止。此外 CompletionService 还提供一个 poll()方法,返回值与 take() 方法一样,不同之处在于它不会阻塞,如果队列为空则立刻返回 null。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354