转自:https://blog.csdn.net/xiao__miao/article/details/86352380

1.近期工作的时候,运维通知一个系统的内存一直在增长,leader叫我去排查,我开始看了一下,没处理,leader自己去看了一下,发现是线程池的问题,我开头没注意那块,一看才发现,确实因为CompletionService里的结果队列引起的。CompletionService里面有一个BlockingQueue维护结果,如果不去取结果就会导致一直里面一直增长

  @SuppressWarnings("unchecked")
public void doExecute(Msg msg, List<Object> actList) {
try {
// 1、开启任务处理mq消息
service.submit(new ActMqTask(msg, actList));
} catch (Exception e) {
LOG.error(prefix + " doExecute is Exception", e);
msg.setStatus(MqMsgStatus.PROCESS);
msg.setResultDesc("消息处理异常" + e.getMessage());
} }

就这段代码,里面没有去消费这个结果队列,导致结果队列一直增长。

已经找原因了,那现在分析下这个ExecutorCompletionService

分析前,我是会默认当前读者是会使用线程池以及了解FutureTask了,不熟悉的源码强烈建议看下这篇博文Java线程池源码分析,读完可能理解就轻松许多

接下来我们就进入分析阶段

1.ExecutorCompletionService

来看下这段代码,网上都有的

public static void main(String[] args) throws InterruptedException, ExecutionException {

    Random random = new Random();
ExecutorService pool = Executors.newFixedThreadPool(3); CompletionService<String> service = new ExecutorCompletionService<String>(pool); for(int i = 0; i<4; i++) { service.submit(() -> {
Thread.sleep(random.nextInt(1000));
System.out.println(Thread.currentThread().getName()+"|完成任务");
return "data"+random.nextInt(10);
});
} for(int j = 0; j < 4; j++) {
Future<String> take = service.take(); //这一行没有完成的任务就阻塞
String result = take.get(); // 这一行在这里不会阻塞,引入放入队列中的都是已经完成的任务
System.out.println("获取到结果:"+result);
}
} CompletionService里的结果集,就是take出来的结果,不是先进先出原则,先完成先出 所以你放入blockingQueue<Future<V>>都是已经完成的执行结果。所以take去拿的时候都是由结果的不会去阻塞 public class ExecutorCompletionService<V> implements CompletionService<V> {
private final Executor executor;
private final AbstractExecutorService aes;
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;
}
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}
.......
} 这里主要重写了FutureTask<Void>里的done方法,执行完之后把结果集放入blockQueue里

再贴一段日常的结果集代码,与之对比

public static void main(String[] args) throws InterruptedException, ExecutionException {

    Random random = new Random();
ExecutorService pool = Executors.newFixedThreadPool(5);
List<Future<String>> resultFuture = new ArrayList<>(); for(int i = 0; i<4; i++) {
final int tmp = i;
Future<String> future = pool.submit(() -> {
Thread.sleep(1000+10*tmp);
System.out.println(Thread.currentThread().getName()+"|完成任务");
return "data"+random.nextInt(10);
});
resultFuture.add(future);
}
System.out.println("--------------"); for(Future<String> future:resultFuture) {
String result = future.get();
System.out.println("执行结果"+result);
}
}

区别对比

1.上面这段代码里没有维护一个结果集的队列

2.取出的结果的不同和执行效率的不同。ExecutorCompletionService里拿结果是最快的,他是根据里面的任务完成就取出。而上面那段代码是根据任务先后顺序然后取出结果集。

注意:

一:结果集的顺序,因为ExecutorCompletionService是根据完成的先后,顺序是不定的

CompletionService用法踩坑解决优化的更多相关文章

  1. RabbitMQ延迟消息:死信队列 | 延迟插件 | 二合一用法+踩坑手记+最佳使用心得

    前言 前段时间写过一篇: # RabbitMQ:消息丢失 | 消息重复 | 消息积压的原因+解决方案+网上学不到的使用心得 很多人加了我好友,说很喜欢这篇文章,也问了我一些问题. 因为最近工作比较忙, ...

  2. IDEA打包javaFX及踩坑解决

    开门见山的说,先打包,再说坑. File-->Project Structure --> Artifacts-->(此处点加号)JAR-->From modules with ...

  3. EasyPoi 导入导出Excel时使用GroupName的踩坑解决过程

    一.开发功能介绍: 简单的一个excel导入功能 二.Excel导入模板(大致模板没写全): 姓名 性别 生日 客户分类 联系人姓名 联系人部门 备注 材料 综合 采购 张三 男 1994/05/25 ...

  4. [Python3]踩坑实录-优化技巧1

    选择合适的数据结构 考虑不同的应用场景,应选择不同的数据结构 比如在查找多于插入的场景中,考虑字典Dict是不是更适合; 因为在Python3中, 字典Dict 通过hash把key映射到hash t ...

  5. ubuntu18.04 搭建scrapy环境(连环踩坑+解决办法)

    ---恢复内容开始--- 预期需求: 打算搭建scrapy环境,基于python3.x的 环境描述: ubuntu18.04自带了python3.6,打算在虚拟环境vlenv中跑scrapy,装好虚拟 ...

  6. 编译课设·CLion到VS踩坑·解决·备忘录

    应试用,VS使用习惯和JB系差别还是蛮大的 打不过他们就加入他们 键位修改 工具-选项 键盘:改keymap 字体和颜色:宋体必改. 自动恢复:自动保存默认3分钟 CMake:自救时可以看一下 键位名 ...

  7. Idea运行支付宝网站支付demo踩坑解决及其测试注意事项

    一.前言 在一些商城网上中,必不可少的是支付,支付宝和微信比较常见,最近小编也是在研究这一块,看看支付宝怎么进行支付的,支付宝给我们提供了demo和沙箱测试.减少我们的申请的麻烦,公钥和秘钥也比之前方 ...

  8. 用户数从 0 到亿,我的 K8s 踩坑血泪史

    作者 | 平名 阿里服务端开发技术专家 导读:容器服务 Kubernetes 是目前炙手可热的云原生基础设施,作者过去一年上线了一个用户数极速增长的应用:该应用一个月内日活用户从零至四千万,用户数从零 ...

  9. 【踩坑经历】一次Asp.NET小网站部署踩坑和解决经历

    2013年给1个大学的小客户部署过一个小型的Asp.NET网站,非常小,用的sqlite数据库,今年人家说要换台服务器,要重新部署一下,好吧,虽然早就过了服务时间,但无奈谁叫人家是客户了,二话不说,上 ...

随机推荐

  1. vue.js(15)--vue的生命周期

    生命周期钩子 生命周期钩子=生命周期函数=生命周期事件 每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听.编译模板.将实例挂载到 DOM 并在数据变化时更新 DOM 等 ...

  2. python 中PIL.Image和OpenCV图像格式相互转换

    PIL.Image转换成OpenCV格式: import cv2 from PIL import Image import numpy   image = Image.open("plane ...

  3. Git的基本操作命令

    Git的基本命令 ## Git命令行 ### 查看配置 ```d git config user.name //查看用户名 git config user.email //查看邮箱 ``` ### 全 ...

  4. 异步json发送put或者delete

    第一种 put请求或者delete请求 直接写发送的情况 //批量删除 function batchDel() { var ids = []; $("#list-table").f ...

  5. MySQL的删除语句

    虽然现在数据库空间越来越大,但处理数据时候还是有要删除的时候,以下整理了一些最常用的删除语句. 分成两种 一个是删除指定数据,另一个删除所有数据. 一.删除指定数据 DELETE FROM 表名 WH ...

  6. tf.clip_by_global_norm

    首先明白这个事干嘛的,在我们做求导的时候,会遇到一种情况,求导函数突然变得特别陡峭,是不是意味着下一步的进行会远远高于正常值,这个函数的意义在于,在突然变得陡峭的求导函数中,加上一些判定,如果过于陡峭 ...

  7. break语句、continue语句、goto语句的用法辨析

    1.break语句 break语句常使用在switch语句.循环体以及if语句中,它的作用是跳出循环,而且只能跳出一层循环. for (i = 0; i < 10; ++j) { for (j ...

  8. CF9D How many trees? (dp)

    这题我想了好久 设 \(f_{i,j}\) 为 \(i\) 结点 \(<=j\) 的方案数 固定根,枚举左右子树,就有: \[f_{i,j}=\sum_{k=0}^{n-1}f_{k,j-1}* ...

  9. bzoj4543 [POI2014]Hotel加强版 长链剖分+树形DP

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4543 题解 这道题的弱化版 bzoj3522 [POI2014]Hotel 的做法有好几种吧. ...

  10. Java常用类库API之数字处理工具类

    数字处理工具类BigDecimal和DecimalFormat Java提供的java.text.DecimalFormat类,帮助我们用最快的速度将数据格式化为我们想要的样子.例如,取两位小数 im ...