一次触发线程池拒绝策略问题的排查

一次触发线程池拒绝策略问题的排查

这个问题的原因是团队中的同事排查出来的,本人觉得这个问题很经典,所以记录一下

线上偶尔会出现线程池拒绝策略触发的告警

每次出现告警都会查看数据库、JVM等监控,系统都是比较平稳的,没有出现请求暴涨的情况,所以认为大概率是线程池配置的问题

分析过程

线程池参数有很多,但最核心的是三个corePoolSize、maximumPoolSize、workQueuequeueCapacity,三者的关系如下

系统通过JDK原生线程池ThreadPoolTaskExecutor设置线程池参数,细细看了这个类的代码,corePoolSize、maxPoolSize、keepAliveSeconds等参数都是正常的设置(set),只有queueCapacity参数做了一些判断,代码如下

如果阻塞队列容量大于0,阻塞队列使用LinkedBlockingQueue,等于0,使用SynchronousQueue

于是做个测试,看看是不是这里导致的

测试代码如下,如果触发了线程池拒绝策略,打印出来

理论上核心线程数有10个,一组执行完,另一组还可以使用核心线程执行,是不需要触发非核心线程的,结果出乎我的意料

queueCapacity等于1的情况

queueCapacity等于0的情况

显然,使用LinkedBlockingQueue,线程复用率不高,导致触发了线程池拒绝策略

那么问题来了,为什么LinkedBlockingQueue线程复用率不高,google搜索了一下

千篇一律都是这种类型的文章,只有结论,没有说为什么,所以只能去看源码

线程池源码

线程池提交任务的代码如下

这段代码跟文章开头那张图是完美对应的,核心代码在于 workQueue.offer(command),因为入队失败,才会 addWorker(command, false)创建非核心线程

所以为什么SynchronousQueue会入队成功,LinkedBlockingQueue会入队失败,只能看两个队列的源码,比较一下,下面简单说说两者的源码

SynchronousQueue源码

SynchronousQueue代码还是蛮复杂的,有公平非公平两种模式,默认是非公平,如下图

  1. SynchronousQueue每个节点有三个状态,分别是未匹配的消费者、未匹配的生产者、正在匹配另一个生产者或消费者
  2. 当队列为空时,生产者或者消费者会直接进入栈中,自旋或者阻塞等待生产者或者消费者匹配
  3. 当栈中有生产者,消费者进入会去匹配该生产者,或者当栈中有消费者,生产者进入会进行匹配,匹配完成一起出栈
  4. 当栈中有生产者与消费者正在匹配,又进来了一个生产者或者消费者线程,它会协助两者匹配

看上是不是很懵,其实我一开始也很懵,juc包的代码是很难理解的,下面是详细的代码

LinkedBlockingQueue 源码解析

我的一点猜想

  1. LinkedBlockingQueue存取节点使用锁进行了互斥,性能没有SynchronousQueue好,所以同样的数据SynchronousQueue处理起来更快
  2. SynchronousQueue支持多线程协作栈中数据,可以充分利用多线程的好处,不像LinkedBlockingQueue,队列到达上限,直接返回false,任务只能创建非核心线程

由于本人水平有限,无法在多线程的环境下验证自己的猜想,期待大佬指点一二

参考文档

tech.meituan.com/2020/04/02/...

相关推荐
山峰哥2 分钟前
JOIN - 多表关联的魔法——3000字实战指南
java·大数据·开发语言·数据库·sql·编辑器
jghhh013 分钟前
C#中实现不同进程(EXE)间通信的方案
java·单例模式·c#
Mr.朱鹏4 分钟前
Spring Boot 配置文件加载顺序与优先级详解
java·spring boot·后端·spring·maven·配置文件·yml
m0_579146654 分钟前
Maven 编译的settings配置和pom、idea配置关系
java·maven·intellij-idea
洛阳泰山8 分钟前
一个人,一个项目,一年的坚持:关于我的 2025年 技术突围之路
java·人工智能·spring boot
虫小宝12 分钟前
企业微信API接口的Java SDK封装:可复用、可测试的工具类设计方法
java·开发语言·企业微信
hanjq_code12 分钟前
java使用阿里的easyExcel解决把excel每行的数据转成excel表格格式数据并打包成ZIP下载
java·开发语言·excel
独自破碎E18 分钟前
包含min函数的栈
android·java·开发语言·leetcode
沛沛老爹18 分钟前
基于Spring Retry实现的退避重试机制
java·开发语言·后端·spring·架构
没有bug.的程序员19 分钟前
Java IO 与 NIO:从 BIO 阻塞陷阱到 NIO 万级并发
java·开发语言·nio·并发编程·io流·bio