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

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

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

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

每次出现告警都会查看数据库、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/...

相关推荐
曾昭武3 分钟前
IDEA怎么汉化&idea中文改回英文版
java·intellij-idea·idea汉化·idea怎么汉化·idea转回英文
信徒_1 小时前
SpringBoot 自动装配流程
java·spring boot·后端
小薛博客2 小时前
4、前后端联调文生文、文生图事件
java·ai
蓝婷儿2 小时前
前端面试每日三题 - Day 34
前端·面试·职场和发展
愛~杦辷个訾3 小时前
芋道项目,商城模块数据表结构
java·sql·芋道·yudao-cloud·芋道商城
C_Liu_7 小时前
C语言:深入理解指针(5)
java·c语言·算法
佛祖保佑永不宕机7 小时前
麒麟系统ARM64架构部署mysql、jdk和java项目
java·arm
qqxhb8 小时前
零基础学Java——第十一章:实战项目 - 桌面应用开发(JavaFX入门)
java·开发语言·javafx
hy.z_7778 小时前
【数据结构】链表 LinkedList
java·数据结构·链表
Akiiiira9 小时前
【数据结构】队列
java·开发语言·数据结构