分享一个JDK批量异步任务工具CompletionService,超好用
摘要:当需要批量提交异步任务,推荐CompletionService。CompletionService将线程池Executor和阻塞队列融合,让批量异步任务管理更简单。
本文分享自华为云社区《JDK批量异步任务最强工具CompletionService》,作者: JavaEdge。
如何优化一个查询各个价格接口的代码?若使用“ThreadPoolExecutor+Future”,可能优化如下:
三个线程异步执行查询价格,通过三次调用Future的get()方法获取结果,之后将查询结果保存在MySQL。

若获取price1耗时很长,那么即便获取price2耗时短,也无法让保存price2的操作先执行,因为主线程都阻塞在 f1.get()。这种问题如何解决呢?
加个阻塞队列! 获取到price1、2、3都进入阻塞队列,然后在主线程消费阻塞队列,就能保证先获取到的价格先保存:

CompletionService实现查询价格
实际开发推荐CompletionService,不但能帮你解决先获取到的价格先保存,还能精简代码。
CompletionService内部维护了一个阻塞队列,当任务执行结束就把任务的执行结果入队,但CompletionService是把任务执行结果的Future对象入队,而上面demo是把任务最终执行结果入队。
创建CompletionService
CompletionService接口的实现类是ExecutorCompletionService,这个实现类的构造方法有两个,分别是:
- ExecutorCompletionService(Executor executor);

- ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>> completionQueue)

这俩构造器都需要传入一个线程池,若不指定completionQueue,默认使用无界LinkedBlockingQueue。任务执行结果的Future对象就是加入到completionQueue中。
学长~你直接给我写段代码解释清楚点呗 ~
让我们试着利用CompletionService实现高性能的查询房价系统。 之后通过CompletionService#submit()提交三个询价操作,这三个询价操作将会被CompletionService异步执行。
最后CompletionService#take()获取一个Future对象(加入到阻塞队列的是任务执行结果的Future对象),调用Future#get()就能返回执行结果。

CompletionService接口
CompletionService接口提供的方法

submit()相关的方法有两个:
- 一个方法参数是Callable<V> task
- 一个方法有两个参数,分别是Runnable task和V result,该方法类似于ThreadPoolExecutor的 <T> Future<T> submit(Runnable task, T result) ,
CompletionService实现Dubbo#Forking Cluster
Dubbo中有一种叫做Forking的集群模式,这种集群模式下,支持并行调用多个查询服务,只要有一个成功返回结果,整个服务即可返回。例如你需要提供一个地址转坐标的服务,为了保证该服务的高可用和性能,可并行调用3个地图服务商的API,然后只要有1个正确返回了结果r,那么地址转坐标这个服务就可以直接返回r了。这种集群模式可以容忍2个地图服务商服务异常,但缺点是消耗的资源偏多。
geocoder(addr) {
// 并行执行以下3个查询服务,
r1=geocoderByS1(addr);
r2=geocoderByS2(addr);
r3=geocoderByS3(addr);
// 只要r1,r2,r3有一个返回
// 则返回
return r1|r2|r3;
}
利用CompletionService可快速实现 Forking 这种集群模式,比如下面示例代码。 首先创建一个线程池executor 、一个CompletionService对象cs和一个Future<Integer>类型的列表 futures,每次通过调用CompletionService的submit()方法提交一个异步任务,会返回一个Future对象,把这些Future对象保存在列表futures中。通过调用 cs.take().get(),我们能够拿到最快返回的任务执行结果,只要我们拿到一个正确返回的结果,就可以取消所有任务并且返回最终结果了。
// 创建线程池
ExecutorService executor =
Executors.newFixedThreadPool(3);
// 创建CompletionService
CompletionService<Integer> cs =
new ExecutorCompletionService<>(executor);
// 用于保存Future对象
List<Future<Integer>> futures =
new ArrayList<>(3);
// 提交异步任务,并保存future到futures
futures.add(
cs.submit(()->geocoderByS1()));
futures.add(
cs.submit(()->geocoderByS2()));
futures.add(
cs.submit(()->geocoderByS3()));
// 获取最快返回的任务执行结果
Integer r = 0;
try {
// 只要有一个成功返回,则break
for (int i = 0; i < 3; ++i) {
r = cs.take().get();
// 简单地通过判空来检查是否成功返回
if (r != null) {
break;
}
}
} finally {
// 取消所有任务
for(Future<Integer> f : futures)
f.cancel(true);
}
// 返回结果
return r;
总结
当需要批量提交异步任务,推荐CompletionService。CompletionService将线程池Executor和阻塞队列融合,让批量异步任务管理更简单。
CompletionService能让异步任务的执行结果有序化,先执行完的先进入阻塞队列,利用该特性,可以轻松实现后续处理的有序性,避免无谓等待,同时还可以快速实现诸如Forking Cluster这样的需求。
CompletionService的实现类ExecutorCompletionService,需要你自己创建线程池,虽看上去有些啰嗦,但好处是你可以让多个ExecutorCompletionService的线程池隔离,这种隔离性能避免几个特别耗时的任务拖垮整个应用的风险。
分享一个JDK批量异步任务工具CompletionService,超好用的更多相关文章
- 分享一个安卓中异步获取网络图片并自适应大小的第三方程序(来自github)
安卓中获取网络图片,生成缓存 用安卓手机,因为手机流量的限制,所以我们在做应用时,要尽量为用户考虑,尽量少耗点用户的流量,而在应用中网络图片的显示无疑是消耗流量最大的,所以我们可以采取压缩图片或者将图 ...
- 分享一个web应用程序池管理工具
因为项目在联调阶段由于各种各样的原因需要重启应用程序池,而调试服务器基本都需要远登操作.同样的情况也会发生在线上,如果公司权限控制得比较严格,每次都要多部门的服务器权限申请的话有点麻烦, 所以抽点时间 ...
- 分享一个关于jackson的Json工具类
直接贴代码: import org.codehaus.jackson.map.DeserializationConfig.Feature; import org.codehaus.jackson.ma ...
- 分享一个jdk源码链接
请查看下面的链接:http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/bcba89ce0a8c/src/share/classes/,进入页面后,点击列表中 ...
- [W3bsafe]分享一个爬SQL注入漏洞的工具
分享一个爬SQL注入的工具 本文转自:i春秋社区由团队核心成员若间开发把工具放到E盘的一个文件夹 他会自动生成一个文本文件 Result.txt 最大页数 自己想弄填多少就填多少关键词 注入点关键词 ...
- 重复造轮子,编写一个轻量级的异步写日志的实用工具类(LogAsyncWriter)
一说到写日志,大家可能推荐一堆的开源日志框架,如:Log4Net.NLog,这些日志框架确实也不错,比较强大也比较灵活,但也正因为又强大又灵活,导致我们使用他们时需要引用一些DLL,同时还要学习各种用 ...
- 分享一个批量导出当前实例下的所有linkedserver脚本
分享一个批量导出当前实例下的所有linkedserver脚本 很多时候,我们都需要导出实例下面的登录用户,job,linkedserver等等 导出job比较复杂,下午写了一个脚本把所有的linked ...
- 分享一个开源的网盘下载工具BaiduPCS-Go
大家在使用网盘的时候,一定忍受不了限速下载的速度.今天给大家分享一个开源的网盘下载项目BaiduPCS-Go.Go语言编写,仿 Linux shell 文件处理命令的百度网盘命令行客户端.多平台支持, ...
- 分享一个Snackbar工具类 SnackbarUtils;
分享一个Snackbar工具类,源代码也是在Github上面找的,自己做了一下修改: 功能如下: 1:设置Snackbar显示时间长短 1.1:Snackbar.LEN ...
随机推荐
- KestrelServer详解[2]: 网络链接的创建
<注册监听终结点(Endpoint)>已经详细讲述了如何使用KestrelServer,现在我们来简单聊聊这种处理器的总体设计和实现原理.当KestrelServer启动的时候,注册的每个 ...
- protocol 协议语言介绍
Protocol Buffer是Google提供的一种数据序列化协议,是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,很适合做数据存储或 RPC 数据交换格式.它可用于通讯协议.数据存储 ...
- java 8 一个list过滤另外一个list
- 编写 Java 程序时, 如何在 Java 中创建死锁并修复它?
经典但核心Java面试问题之一.如果你没有参与过多线程并发 Java 应用程序的编码,你可能会失败.
- jpa CriteriaQueryNo explicit selection and an implicit one could not be determined
java.lang.IllegalArgumentException: Error occurred validating the Criteria ... Caused by: java.lang. ...
- 解释 WEB 模块?
Spring 的 WEB 模块是构建在 application context 模块基础之上,提供一个适 合 web 应用的上下文.这个模块也包括支持多种面向 web 的任务,如透明地处理 多个文件上 ...
- osi七层模型&tcp/udp
1.TCP/UDP协议 1.1 TCP协议 可靠,速度慢,全双工通信 建立连接三次握手,断开连接四次挥手 建立起链接之后,发送每条消息都有回执,为了保证数据的完整性,还有重传机制 数据传输:有收必有发 ...
- (stm32f103学习总结)—USART串口通信
一. USART简介 USART即通用同步异步收发器,它能够灵活地与外部设备进行全双工 数据交换,满足外部设备对工业标准 NRZ 异步串行数据格式的要求. UART即通用异步收发器,它是在USART基 ...
- RESTful API/Web API
Microsoft REST API Guidelines Are Not RESTful White House Web API Standards Microsoft REST API Guide ...
- 浏览器默认解析文档模式IE7
背景:实习期间开发了一套页面,兼容到IE8+,可是在给产品体验时,他的QQ浏览器默认解析到了文档模式IE7,出现了页面的兼容性问题.问题排查:QQ浏览器目前有两个模式,极速模式和兼容模式,其中极速模式 ...