Java多线程之ExecutorCompletionService
创始人
2025-06-01 20:34:57
0

文章目录

  • 1 ExecutorCompletionService
    • 1.1 简介
    • 1.2 原理
    • 1.3 Demo示例
      • 1.3.1 未使用ExecutorCompletionService
      • 1.3.2 使用ExecutorCompletionService
    • 1.4 深入分析说明
      • 1.4.1 所有方法
      • 1.4.2 构造方法
      • 1.4.3 获取方法
      • 1.4.4 提交方法

1 ExecutorCompletionService

1.1 简介

当我们向Executor提交一组任务,并且希望任务在完成后获得结果,此时可以考虑使用ExecutorCompletionService

ExecutorCompletionService实现了CompletionService接口。ExecutorCompletionServiceExecutorBlockingQueue功能融合在一起,使用它可以提交我们的Callable任务。这个任务委托给Executor执行,可以使用ExecutorCompletionService对象的takepoll方法获取结果。

ExecutorCompletionService的设计目的在于提供一个可获取线程池执行结果的功能,这个类采用了装饰器模式,需要用户提供一个自定义的线程池,在ExecutorCompletionService内部持有该线程池进行线程执行,在原有的线程池功能基础上装饰额外的功能。

ExecutorCompletionService 相比之前 Future 相比 ,提供了一个通知机制,将结果统一到一个队列,当前提交任务不会阻塞获取,从另一个队列中阻塞获取。

1.2 原理

在这里插入图片描述

执行原理:

  • 在使用ExecutorCompletionService时需要提供一个自定义的线程池Executor,构造ExecutorCompletionService。同时,也可以指定一个自定义的队列作为线程执行结果的容器,当线程执行完成时,通过重写FutureTask#done()将结果压入队列中。
  • 当用户把所有的任务都提交了以后,可通过ExecutorCompletionService#poll方法来弹出已完成的结果,这样做的好处是可以节省获取完成结果的时间。

1.3 Demo示例

1.3.1 未使用ExecutorCompletionService

public class ExecutorCompletionServiceDemo {public static void main(String[] args) {//这里只是为了方便,真正项目中不要这样创建线程池ExecutorService executorService = Executors.newFixedThreadPool(5);List> list = new ArrayList<>();Future future1 = executorService.submit(() -> {System.out.println("执行任务1开始");try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行任务1结束");return "任务1执行成功";});list.add(future1);Future future2 = executorService.submit(() -> {System.out.println("执行任务2开始");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行任务2结束");return "任务2执行成功";});list.add(future2);Future future3 = executorService.submit(() -> {System.out.println("执行任务3开始");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行任务3结束");return "任务3执行成功";});list.add(future3);for (int i = 0; i < list.size(); i++) {String s = null;try {s = list.get(i).get();} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}System.out.println(s);}executorService.shutdown();}
}

在这里插入图片描述

我们可以看到三个任务的执行结果会按照提交顺序的任务执行时间进行堵塞依次获取结果;我们提交到线程池中,通过Futrue类的get()方法,会造成堵塞,需要先等执行任务1的线程结束返回结果,才会进行获取下一个任务的执行的结果,那边后面的任务先于任务一执行结束;当然如果工作中我们不需要获取多个任务执行的结果,我们可以采用上面的实现方式去进行并行处理任务;

1.3.2 使用ExecutorCompletionService

如果我们要获取到并行处理任务的结果快慢来进行一些处理,我们就可以使用到ExecutorCompletionService来进行实现;我们来使用ExecutorCompletionService类将线程池进行包装处理下,然后进行提交任务;

public class ExecutorCompletionServiceDemo {public static void main(String[] args) {//这里只是为了方便,真正项目中不要这样创建线程池ExecutorService executorService = Executors.newFixedThreadPool(5);ExecutorCompletionService completionService = new ExecutorCompletionService<>(executorService);completionService.submit(() -> {System.out.println("执行任务1开始");try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行任务1结束");return "任务1执行成功";});completionService.submit(() -> {System.out.println("执行任务2开始");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行任务2结束");return "任务2执行成功";});completionService.submit(() -> {System.out.println("执行任务3开始");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("执行任务3结束");return "任务3执行成功";});for (int i = 0; i < 3; i++) {try {String result = completionService.take().get();System.out.println(result);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}executorService.shutdown();}
}

在这里插入图片描述

1.4 深入分析说明

解决批量提交任务办法就是使用使用 ExecutorCompletionService,异步通知返回。

1.4.1 所有方法

当前类提供的方法
在这里插入图片描述

1.4.2 构造方法

提供的两个构造函数,一个可以指定返回阻塞队列,另一个使用默认的。另外都需要提供一个线程池进来
在这里插入图片描述

1.4.3 获取方法

提供了三个获取方法,可以看到都是从队列中获取
在这里插入图片描述

take获取:谁先执行完 谁先出来 take() 获取时候回阻塞 也可以通过Poll方法获取
polltake 区别在于 poll 可以执行超时时间,可以看到,谁先执行结束 谁先出来。

1.4.4 提交方法

两个提交任务方法
在这里插入图片描述
如何执行任务结果放入队列呢?

可以看到是将 执行结果放入队列中。
内部实现了异步执行接口,以及重写了它的done方法
在这里插入图片描述

相关内容

热门资讯

原创 一... 标题:一份绝妙的鳗鱼饭是如何炼成的? 在追求极致美味的道路上,我们总在探寻那些能够触动味蕾的秘密。...
豆芽菜只知道泡水保存? 专家分... 想要延长豆芽菜的保存期限,日本料理达人分享「用50度水浸泡2分钟」就能保存一周还能维持蔬菜脆度。 豆...
原创 豆... 标题:豆腐与它绝配,天冷热乎乎来一锅,一咬满口爆鲜汁,连锅一起吃 在寻找那一份能够唤醒味蕾的美食时...
原创 大... 相比南方一个市一个主打汤,我觉得北方的汤要简单太多了。 基本都是清水煮汤,味道全凭对食材的拿捏。 ...
原创 6... 暑气蒸腾的季节,厨房灶火前多站片刻都是煎熬。偏是此时,一碗清清爽爽的瓠子鸡蛋汤端上桌,那水润鲜亮的汤...
原创 豆... 标题:豆腐新吃法,1瓶可乐就搞定,比可乐鸡还好吃,做法超简单 在这个快节奏的时代,我们总是在寻找那...
原创 鸡... 标题:鸡蛋的新做法,只需3步,口感和颜值并存,招待客人特别有面子。 在美食的世界里,鸡蛋以其简单而...
原创 1... 火爆全国的湘菜,迎来了新拐点。 爆红的湘菜有了新挑战。 近几年,湘菜成了中餐赛道的焦点,“排队王...
原创 强... 鸡蛋抱豆腐这道菜,简直是我家餐桌上的"核武器级"下饭神器!每次掀开锅盖的瞬间,金灿灿的鸡蛋裹着嫩滑的...
原创 一... 标题:一根香肠,三个鸡蛋,做道嫩滑可口又营养的早餐,10分钟搞定! 在忙碌的早晨,我们总是渴望一份...
安徽涡阳:粽香传古韵 民俗庆端... 端午期间,安徽涡阳县各地纷纷开展丰富多彩的活动,以粽香传递千年古韵,用民俗共庆传统端阳,让古老节日焕...
原创 舌... 咱们总说 “民以食为天”,其实每一口吃下去的,可不只是填饱肚子的东西,更是一部藏在舌尖上的文明史。今...
原创 炒... 说起炒豆角,多少主妇翻过车?不是炒得蔫黄软烂失了魂,就是硬邦邦一股子生豆腥气,嚼得腮帮子累!更有人图...
原创 一... 标题:一块南瓜,做出的糕点,香软Q弹,热量也不高,有没有诱惑到你? 在探索美食的旅程中,我们总能找...
走进乌兰布统 感受雄浑壮阔的塞... 本文转自:人民网-内蒙古频道6月2日,航拍克什克腾旗乌兰布统草原美景。李富摄在内蒙古自治区赤峰市克什...
重庆五天四晚旅游攻略,两个人去... 重庆五天四晚旅游攻略,两个人去重庆玩五天要消费多少? 最近,我和朋友一直计划着一场说走就走的旅行,而...
原创 黄... 斑马消费 杨伟 2025年A股酒水板块“冰火两重天”,白酒承压,啤酒失速,黄酒却异军突起! Wind...
原创 4... 在城市的喧嚣中,我们常常忽略了那些默默付出的劳动者。今天,我要讲述的是一位来自农村的大姐,她的故事让...
原创 鱼... 标题:鱼肉的最新吃法,不蒸不煮不烧,鲜香爽滑,可以吃下两大碗米饭! 在探索美食的旅途中,我们总在寻...