背景

最近在项目中看到太多后台task中使用Executor框架,提交任务后,把future都一个个加入到list,再一个个get这些future的代码。

这个的问题在于一方面没有时限,可能会被某些运行缓慢的future拖很久。即便使用带超时控制的get方法,这样加入list再get的做法依然很繁琐。

其实在《Java并发编程实战》或者《Java多线程编程的艺术》这些书中都介绍过JDK提供了CompletionService接口。

例程

JDK为我们提供的CompletionService接口的默认实现是java.util.concurrent.ExecutorCompletionService。在它的Java Doc中已经给出两个demo例程。

void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException, ExecutionException {
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);
}
}

上面的例程展示了CompletionService的基本使用。它的实现融合了Executor和BlockingQueue。可以看到任务的执行依托于内部的Executor,而一个任务完成后会被加到阻塞队列中,调用线程可以及时获取到新完成的任务。

如下所示为Java Doc中另一个例程。

void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException {
CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e);
int n = solvers.size();
List<Future<Result>> futures
= new ArrayList<Future<Result>>(n);
Result result = null;
try {
for (Callable<Result> s : solvers)
futures.add(ecs.submit(s));
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);
}

上面的例程中,用于获取第一个返回值不为null的任务结果,并取消其他任务。

原理

ExecutorCompletionService的源码实现非常简单。

内部就三个东西:

// 构造方法传入的executor实例。
private final Executor executor;
// 如果构造方法传入的executor实例是AbstractExecutorService子类,则类型转化后保存。
private final AbstractExecutorService aes;
// 用于保存完成的future,所谓完成可以是有异常或者已经取消。
private final BlockingQueue<Future<V>> completionQueue;

内部最核心的嵌套类是:

private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}

QueueingFuture内部组合了一个RunnableFuture,然后在构造方法中通过父类FutureTask的构造方法,将之转化为FutureTask的内部callable。

它对FutureTask中的钩子方法done进行了覆盖,将构造函数传入的RunnableFuture在完成后加到阻塞队列中。

FutureTask的done方法会在任务正常完成/发生异常/被取消后被调用。更多源码可以参考我的FutureTask源码解读

剩余提交任务的各种submit方法,无非就是在原来的FutureTask上用QueueingFuture套上一套,实现任务在完成后加到阻塞队列的逻辑。

而获取任务的take/poll方法的实现就是调用内部阻塞队列而已。

至此,全部讲完了。

CompletionService简讲的更多相关文章

  1. Python Web学习笔记之递归和迭代的区别

    电影故事例证:迭代——<明日边缘>递归——<盗梦空间> 迭代是更新变量的旧值.递归是在函数内部调用自身. 迭代是将输出做为输入,再次进行处理.比如将摄像头对着显示器:比如镜子对 ...

  2. 【深度学习大讲堂】首期第一讲:人工智能的ABCDE 第二部分:简谈当前AI技术与发展趋势

    (完)

  3. .NET简谈接口

    自从面向对象开发方式的出现,抽象的概念就开始日新月异的发展,面向对象编程.面向接口编程.面向组件编程等等:这一系列的概念都是软件工程所追求的思想范畴,高类聚低耦合. 今天我要简谈的是面向对象里面非常重 ...

  4. Vim,极简使用教程,让你瞬间脱离键鼠切换的痛苦

    注:看大家对Vim仇恨极大,其实它只是一种文本操作方式,可以减少键鼠的切换,从而让编辑文本的操作更迅捷.并不等同于IDE,在我看来,它们是两个是包含关系,IDE可以有Vim编辑模式.Vim或许可以通过 ...

  5. js 简繁体字转换

    有些项目需要用到简体和繁体两种字体,在js前台进行转换比较方便而且显示速度没有延时 是一个比较好的解决方案. var _isFT_CS = 0// 简体 var _isFT_CT = 1// 繁体 v ...

  6. 【英语魔法俱乐部——读书笔记】 1 初级句型-简单句(Simple Sentences)

    第一部分 1 初级句型-简单句(Simple Sentences):(1.1)基本句型&补语.(1.2)名词短语&冠词.(1.3)动词时态.(1.4)不定式短语.(1.5)动名词.(1 ...

  7. 在Web应用中接入微信支付的流程之极简清晰版

    在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...

  8. SQL简繁转换函数

    declare @jall nvarchar(4000),@fall nvarchar(4000) select @jall=N'啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊 ...

  9. 在Web应用中接入微信支付的流程之极简清晰版 (转)

    在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...

随机推荐

  1. [转]关于Linux安装mysql默认配置文件位置

    本文转自:https://blog.csdn.net/smile___you/article/details/54409073 在linux下面安装mysql如果在/etc下面没有存在my.cnf配置 ...

  2. ASP.NET MVC提交一个较复杂对象至WCF Service

    前一篇<jQuery.Ajax()执行WCF Service的方法>http://www.cnblogs.com/insus/p/3727875.html 我们有练习在asp.net mv ...

  3. PetaPoco源代码学习--2.TableInfo、ColumnInfo类和Cache类

    当把常用的特性填写到POCO实体类时,执行数据库操作时,需要根据实体类上的特性信息进行相应的操作,PetaPoco中的TableInfo和ColumnInfo类就是用来保存实体类上的特性信息. Tab ...

  4. 【Java并发编程】7、线程池

    1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...

  5. 使用 ActiveMQ 实现JMS 异步调用

    目录 简介 启动 ActiveMQ 服务器 查看控制台 ActiveMQ 的消息通道 Queue Topic 比较 开发生产者和消费者 开发服务端(消费者) 开发客户端(生产者) 参考 简介 服务之间 ...

  6. 设计模式-组合模式(Composite)

    一.概念 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 二.模式动机 组合模式,通过设计一个抽像的组件类,使它既代表叶子对象,又代表组合对 ...

  7. Android四大组件-Service

    http://blog.csdn.net/guolin_blog/article/details/11952435 http://www.jianshu.com/p/eeb2bd59853f 概述 定 ...

  8. POJ2104(可持久化线段树)

    K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 58759   Accepted: 20392 Ca ...

  9. Postman Postman测试接口之POST提交本地文件数据

    Postman测试接口之POST提交本地文件数据   by:授客 QQ:1033553122 本文主要是针对用Postman POST提交本地文件数据的方法做个简单介绍 举例: 文件同步接口 接口地址 ...

  10. MySql 利用mysql&mysqldum导入导出数据

    MySql 利用mysql&mysqldum导入导出数据 by:授客 QQ:1033553122   测试环境 Linux下测试,数据库MySql 工具 mysqldump,该命令位于mysq ...