当我们向Executor提交一组任务,并且希望任务在完成后获得结果,此时可以考虑使用ExecutorCompletionService。
ExecutorCompletionService实现了CompletionService接口。ExecutorCompletionService将Executor和BlockingQueue功能融合在一起,使用它可以提交我们的Callable任务。这个任务委托给Executor执行,可以使用ExecutorCompletionService对象的take和poll方法获取结果。其中BlockingQueue负责保存计算完成的结果,其基本结构如下:

public class ExecutorCompletionService<V> implements CompletionService<V> {private final Executor executor;private final AbstractExecutorService aes;private final BlockingQueue<Future<V>> completionQueue;public ExecutorCompletionService(Executor executor) {this.executor = executor;this.aes = (executor instanceof AbstractExecutorService) ?(AbstractExecutorService) executor : null;this.completionQueue = new LinkedBlockingQueue<Future<V>>();}
}

ExecutorCompletionService对象使用submit提交任务,任务同样返回一个Future。且在执行任务的时候,Task会被封装成RunnableFuture,RunnableFuture是一个接口,可以是FutureTask,也可以是其他实现了RunnableFuture的类型。进而封装成QueueingFuture交给executor执行。

public Future<V> submit(Callable<V> task) {if (task == null) throw new NullPointerException();RunnableFuture<V> f = newTaskFor(task);executor.execute(new QueueingFuture<V>(f, completionQueue));return f;}private RunnableFuture<V> newTaskFor(Callable<V> task) {if (aes == null)return new FutureTask<V>(task);elsereturn aes.newTaskFor(task);}

QueueingFuture是什么呢?它是FutureTask的子类,主要是重写了FutureTask的done方法,将计算结果放到阻塞队列里。

   private static class QueueingFuture<V> extends FutureTask<Void> {QueueingFuture(RunnableFuture<V> task,BlockingQueue<Future<V>> completionQueue) {super(task, null);this.task = task;this.completionQueue = completionQueue;}//在此处task可以看作是FutureTask.private final Future<V> task;private final BlockingQueue<Future<V>> completionQueue;//将计算结果放到阻塞队列里protected void done() { completionQueue.add(task); }}

注意我们放进队列的是FutureTask.我们从ExecutorCompletionService的take方法里获取到的是一个FutureTask对象,如果想要获取到最终结果,需要调用FutureTask的get方法:

Result r = ecs.take().get();

所以我们可以使用如下代码来使用CompletionService,创建ExecutorCompletionService对象;使用Executor提交任务;使用ExecutorCompletionService的take方法获取FutureTask,然后调用FutureTask的get方法获取结果。假设你有一些任务希望并发的执行,并且向往将结果交给某方法执行,则可以用如下代码来实现:

void solve(Executor e, Collection<Callable<Result>> solvers) {CompletionService<Result> ecs= new ExecutorCompletionService<Result>(e);//循环提交每一个任务   for (Callable<Result> s : solvers)ecs.submit(s);int n = solvers.size();for (int i = 0; i < n; ++i) {Result r = ecs.take().get();if (r != null)use(r);}}}

上面我们在for循环里使用ecs.submit(s)来提交一个任务。ecs.submit返回的是一个Future<Result>对象,所以我们提交了多少次,就产生了多少个Future<Result>对象。所以我们可以使用List集合将这些Future<Result>保存起来,可以解决如下的应用场景,假设您希望使用第一个非空结果,忽略所有遇到异常的任务,以及在第一个任务准备就绪时取消所有其他任务。那么如下代码可以帮助到你。

 void solve(Executor e,Collection<Callable<Result>> solvers)throws InterruptedException {CompletionService<Result> ecs= new ExecutorCompletionService<Result>(e);int n = solvers.size();//用来保存执行的结果Future集合List<Future<Result>> futures = new ArrayList<>(n);//提交任务保存结果for (Callable<Result> s : solvers)futures.add(ecs.submit(s));Result result = null;try {for (int i = 0; i < n; ++i) {try {//获取任务,当有一个任务完成时就退出循环Result r = ecs.take().get();if (r != null) {result = r;break;}} catch (ExecutionException ignore) {}}}finally {//取消其他任务for (Future<Result> f : futures)f.cancel(true);}if (result != null)use(result);}}

java并发编程实战读书笔记 ExecutorCompletionService相关推荐

  1. Java并发编程实战读书笔记

    Java并发编程 标签(空格分隔): 并发 多线程 基础 线程 在执行过程中,能够执行程序代码的一个执行单元,在Java语言中,线程有四种状态:运行,就绪,挂起,结束. 并发特性 原子性 一个操作不会 ...

  2. Java并发编程实战读书笔记三

    第七章 取消和关闭 Java没有提供任何机制来安全的终止线程,虽然 Thread.stop 和 suspend 等方法提供了这样的机制,但由于存在着一些严重的陷,因此应该避免使用 7.1任务取消 7. ...

  3. Java并发编程实战读书笔记(一)——线程安全性、对象共享

    一.线程安全性 一个对象是否需要是线程安全的,取决于它是否被多个线程访问. 当多个线程访问,并且其中有一个执行写入时,必须采用同步机制,Java中主要的同步关键字是 synchronized 独占加锁 ...

  4. Java并发编程实战读书笔记二

    第五章 基础构建模块 5.1 同步容器类 5.1.1 同步容器类的问题 如下,如果list含有10个元素,线程A调用getLast的同时线程B调用deleteLast,那么getLast可能会报Arr ...

  5. Java并发编程实战读书笔记一

    第1章 简介 第2章 线程安全性 1个状态变量线程安全的模式 多个状态变量线程不安全的模式,在A线程lastNumbers.set和lastFactors.set之间B线程进行这两个set就出问题了, ...

  6. java并发编程实践 读书笔记_Java - 并发编程实践(读书笔记)

    [注] 同步机制保证:1)原子性 2)内存可见性: Volatile变量只能保证:1)可见性: - 恰当的同步,同步的弱形式,确保对一个变量的更新以可预见的方式告知其他线程. [注] 用锁来协调访问变 ...

  7. Java 并发编程艺术 读书笔记

    第 1 章 并发编程的挑战 1.1.3 如何减少上下文切换 减少上下文切换的方法有无锁并发编程.CAS 算法.使用最少线程和使用协程. 无锁并发编程.多线程竞争锁时,会引起上下文切换,所以多线程处理数 ...

  8. Java并发编程艺术----读书笔记(二)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a724888/article/details/64214595  java并发编程艺术2 jav ...

  9. JAVA并发编程艺术读书笔记(1,2章节)

    第一章 并发编程的挑战 为什么要使用并发编程? 主要是为了更有效地利用资源.即使是单核的CPU也可以多线程执行程序,多线程实际上是CPU分配时间片给各个线程,因为时间片非常短,所以看起来就像在同事执行 ...

  10. Java并发编程艺术读书笔记

    1.多线程在CPU切换过程中,由于需要保存线程之前状态和加载新线程状态,成为上下文切换,上下文切换会造成消耗系统内存.所以,可合理控制线程数量. 如何控制: (1)使用ps -ef|grep appn ...

最新文章

  1. 容器云原生DevOps学习笔记——第二期:如何快速高质量的应用容器化迁移
  2. 用g++编译C++ 的流程示例如下
  3. SGML与HTML、XML
  4. leetcode算法题--数字序列中某一位的数字
  5. background 旋转_基于HTML5 Canvas 实现矢量工控风机叶轮旋转
  6. 无服务器冷启动不是问题-这就是为什么(对于大多数应用程序)
  7. linux audio A2B
  8. android nfc开发_意法半导体NFC产品线完整 满足多元应用_企业动态_新闻中心
  9. 【图像融合】基于matlab GUI像素点图像融合【含Matlab源码 783期】
  10. 如何在远程桌面连接的服务器上访问本地磁盘
  11. pt电阻测温c语言算法,铂电阻测温仪的设计与实现
  12. 小a的旅行计划(BM模板)
  13. vss服务器状态失败_VSS常犯错误(转载)
  14. 江南大学物联网工程学院数据库课程实验一作业1实验报告
  15. 三年高级开发,六年成为架构师,到CTO我用了12年
  16. iOS 模拟器 获取位置 设置自定义位置
  17. 正则表达式(regex,RE)
  18. 求建议:二本计算机专业,考研还是就业做开发?
  19. 树莓派接手机屏幕_如何将树莓派连接到手机屏幕
  20. 接口开发及技术负责人的职责随笔

热门文章

  1. Java进阶:The driver is automatically registered via the SPI and manual loading of the driver class is
  2. Java集合框架源码解读(4)——WeakHashMap
  3. 论文笔记_S2D.58_2018-ICRA-基于直接法的使用LiDAR稀疏深度的视觉-激光雷达SLAM
  4. 光学相干断层成像术(OCT)
  5. 如何在 GitHub 上面精准搜索开源项目?
  6. .NET、ASP.NET控件及源码大汇总 最新最全哦
  7. 【docker】docker持续集成CI/持续部署CD
  8. MySQL时区错误导致server time zone value 'Öйú±ê׼ʱ¼ä' 错误
  9. 《用户体验要素——以用户为中心的产品设计》整理
  10. jQuery AJAX 方法