CompletionService简讲
背景
最近在项目中看到太多后台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简讲的更多相关文章
- Python Web学习笔记之递归和迭代的区别
电影故事例证:迭代——<明日边缘>递归——<盗梦空间> 迭代是更新变量的旧值.递归是在函数内部调用自身. 迭代是将输出做为输入,再次进行处理.比如将摄像头对着显示器:比如镜子对 ...
- 【深度学习大讲堂】首期第一讲:人工智能的ABCDE 第二部分:简谈当前AI技术与发展趋势
(完)
- .NET简谈接口
自从面向对象开发方式的出现,抽象的概念就开始日新月异的发展,面向对象编程.面向接口编程.面向组件编程等等:这一系列的概念都是软件工程所追求的思想范畴,高类聚低耦合. 今天我要简谈的是面向对象里面非常重 ...
- Vim,极简使用教程,让你瞬间脱离键鼠切换的痛苦
注:看大家对Vim仇恨极大,其实它只是一种文本操作方式,可以减少键鼠的切换,从而让编辑文本的操作更迅捷.并不等同于IDE,在我看来,它们是两个是包含关系,IDE可以有Vim编辑模式.Vim或许可以通过 ...
- js 简繁体字转换
有些项目需要用到简体和繁体两种字体,在js前台进行转换比较方便而且显示速度没有延时 是一个比较好的解决方案. var _isFT_CS = 0// 简体 var _isFT_CT = 1// 繁体 v ...
- 【英语魔法俱乐部——读书笔记】 1 初级句型-简单句(Simple Sentences)
第一部分 1 初级句型-简单句(Simple Sentences):(1.1)基本句型&补语.(1.2)名词短语&冠词.(1.3)动词时态.(1.4)不定式短语.(1.5)动名词.(1 ...
- 在Web应用中接入微信支付的流程之极简清晰版
在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...
- SQL简繁转换函数
declare @jall nvarchar(4000),@fall nvarchar(4000) select @jall=N'啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊 ...
- 在Web应用中接入微信支付的流程之极简清晰版 (转)
在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...
随机推荐
- [转]csv文件导入Mysql
本文转自:https://blog.csdn.net/quiet_girl/article/details/71436108 本篇博客主要讲将csv文件导入Mysql的方法(使用命令行). Step1 ...
- HLOCAL 初探
首先看一段程序,输出的结果为两个相同的整数(这两个整数是内存地址). #include "stdafx.h" #include <windows.h> int _tma ...
- PetaPoco源代码学习--0.目录贴
2017年3季度后,以人力外包的形式派驻到甲方单位进行项目救急时,接触到了甲方单位的ASP.NET MVC项目的ORM框架,它以PetaPoco(2012年的老版本)进行改造升级的,当初就想学习一下这 ...
- sqlhelper中事务的简单用法
sql1="INSERT INTO tablename(Id,col1,col2) VALUES(@Id,@col1,@col2) update tablename2 set col=@co ...
- 使用 SQLServer On Linux 作为开发数据库遇到的编码问题
在维护旧的项目的时候,遇到了使用 varchar 类型存储中文的情况,本地数据库是直接使用来自上古时期的 T-SQL 创建的.为了方便,我就没有安装 Windows 版的 SQLServer,直接用的 ...
- [日常] Go语言圣经-函数多返回值习题
Go语言圣经-函数多返回值1.在Go中,一个函数可以返回多个值2.许多标准库中的函数返回2个值,一个是期望得到的返回值,另一个是函数出错时的错误信息3.如果一个函数将所有的返回值都显示的变量名,那么该 ...
- [javaSE] 看博客学习多线程的创建方式和优劣比较和PHP多线程
通过实现Runnable接口创建线程 获取Thread对象,new出来,构造函数参数:Runnable对象 Runnable是一个接口,定义一个类MyRunnable实现Runnable接口,实现ru ...
- 【Dubbo&&Zookeeper】1、Dubbo与Zookeeper、SpringMVC整合和使用(负载均衡、容错)
转自:http://blog.csdn.net/congcong68/article/details/41113239 互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架 ...
- CA210彩分仪校准步骤
1.menu(space key)2.space (08307009 U) 按键2次)->(EXT)3.BLUE(按键4次)->PAL4.enter5.0校准(0对准CAL(按住探头)出现 ...
- 从零开始学习html(九)CSS的继承、层叠和特殊性
一.继承 <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" co ...