一次 HashSet 所引起的并发问题
背景
上午刚到公司,准备开始一天的摸鱼之旅时突然收到了一封监控中心的邮件。
心中暗道不好,因为监控系统从来不会告诉我应用完美无 bug,其实系统挺猥琐。
打开邮件一看,果然告知我有一个应用的线程池队列达到阈值触发了报警。
由于这个应用出问题非常影响用户体验;于是立马让运维保留现场 dump 线程和内存同时重启应用,还好重启之后恢复正常。于是开始着手排查问题。
分析
首先了解下这个应用大概是做什么的。
简单来说就是从 MQ 中取出数据然后丢到后面的业务线程池中做具体的业务处理。
而报警的队列正好就是这个线程池的队列。
跟踪代码发现构建线程池的方式如下:
ThreadPoolExecutor executor = new ThreadPoolExecutor(coreSize, maxSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());; put(poolName,executor);
采用的是默认的 LinkedBlockingQueue 并没有指定大小(这也是个坑),于是这个队列的默认大小为 Integer.MAX_VALUE。
由于应用已经重启,只能从仅存的线程快照和内存快照进行分析。
内存分析
先利用 MAT 分析了内存,的到了如下报告。
一次 HashSet 所引起的并发问题
其中有两个比较大的对象,一个就是之前线程池存放任务的 LinkedBlockingQueue,还有一个则是 HashSet。
当然其中队列占用了大量的内存,所以优先查看,HashSet 一会儿再看。
由于队列的大小给的够大,所以结合目前的情况来看应当是线程池里的任务处理较慢,导致队列的任务越堆越多,至少这是目前可以得出的结论。
线程分析
再来看看线程的分析,这里利用 fastthread.io 这个网站进行线程分析。
因为从表现来看线程池里的任务迟迟没有执行完毕,所以主要看看它们在干嘛。
正好他们都处于 RUNNABLE 状态,同时堆栈如下:
发现正好就是在处理上文提到的 HashSet,看这个堆栈是在查询 key 是否存在。通过查看 312 行的业务代码确实也是如此。
这里的线程名字也是个坑,让我找了好久。
定位
分析了内存和线程的堆栈之后其实已经大概猜出一些问题了。
这里其实有一个前提忘记讲到:
这个告警是凌晨三点发出的邮件,但并没有电话提醒之类的,所以大家都不知道。
到了早上上班时才发现并立即 dump 了上面的证据。
所有有一个很重要的事实:这几个业务线程在查询 HashSet 的时候运行了 6 7 个小时都没有返回。
通过之前的监控曲线图也可以看出:
操作系统在之前一直处于高负载中,直到我们早上看到报警重启之后才降低。
同时发现这个应用生产上运行的是 JDK1.7 ,所以我初步认为应该是在查询 key 的时候进入了 HashMap 的环形链表导致 CPU 高负载同时 1c7c 也进入了死循环。
为了验证这个问题再次 review 了代码。
整理之后的伪代码如下:
//线程池
private ExecutorService executor;
private Set<String> set = new hashSet();
private void execute(){
//从 MQ 中获取数据
String key = subMQ();
executor.excute(new Worker(key)) ;
}
}
public class Worker extends Thread{
private String key ;
public Worker(String key){
this.key = key;[url=mailto:br/>}
@Override}
@Override
- 一次 HashSet 所引起的并发问题
- 一次由查询转换引起的性能问题的分析
- 线程的并发所引起的问题
- 一次任务的实践,解决每秒最大并发次数的问题 -- 生产者消费者模式
- 并发插入引起的死锁问题排查
- PHP Session可能会引起并发问题
- 记一次mysql语句因为字符优先级的问题引起的查询结果不一致问题
- 记一次JS执行顺序引起的问题排查过程
- 支付系统转账过程中并发交易引起的分布式死锁问题
- PHP Session可能会引起并发问题
- NAT并发链接引起的网络延时问题
- MySQL并发引起的死锁问题
- 项目问题总结一、全局变量引起的并发问题
- java并发-记一次统计变量值偏差问题
- 乐观锁的一次并发问题
- PHP Session可能会引起并发问题
- 一次因为数据问题引起的reduce被卡住streaming作业问题排查
- 浅析PHP中Session可能会引起并发问题
- PHP Session可能会引起并发问题
- PHP中Session可能会引起并发问题