JDK17 ThreadPoolExecutor核心线程数无法维持最大值问题排查
问题描述
我创建了两个ThreadPoolExecutor,外层outerThreadPoolExecutor负责向内层innerThreadPoolExecutor提交任务。运行初期内层线程池活跃线程数可达700,但一段时间后降至36。服务器配置为2颗CPU,每颗14核28线程。请问:
- JDK是否会自动优化线程数?
- 导致该问题的原因是什么?
- 如何解决活跃线程数过少的问题?
代码示例
val max = 10 val outerThreadPoolExecutor = ThreadPoolExecutor( max, max, 10, TimeUnit.SECONDS, LinkedBlockingQueue(), DefaultThreadFactory("outer-thread-pool") ) val innerThreadPoolExecutor = ThreadPoolExecutor( 700, 700, 10, TimeUnit.SECONDS, LinkedBlockingQueue(), DefaultThreadFactory("inner-"+Thread.currentThread().name) )
日志信息
[2023-09-01 11:11:49]outer:java.util.concurrent.ThreadPoolExecutor@e768678[Shutting down, pool size = 10, active threads = 10, queued tasks = 9984, completed tasks = 6] [2023-09-01 11:11:49]inner:java.util.concurrent.ThreadPoolExecutor@b169c6[Running, pool size = 700, active threads = 700, queued tasks = 78149, completed tasks = 0] [2023-09-01 11:14:35]outer:java.util.concurrent.ThreadPoolExecutor@e768678[Shutting down, pool size = 10, active threads = 10, queued tasks = 9612, completed tasks = 378] [2023-09-01 11:14:35]inner:java.util.concurrent.ThreadPoolExecutor@b169c6[Running, pool size = 700, active threads = 36, queued tasks = 499796, completed tasks = 4500149]
问题分析与解决方案
一、JDK是否会自动优化线程池线程数?
JDK的ThreadPoolExecutor不会自动调整你设置的核心线程数(corePoolSize)和最大线程数(maximumPoolSize)。从日志可见,内层线程池的pool size始终维持在700,完全符合你设置的corePoolSize=700、maximumPoolSize=700的配置。这里的活跃线程数(active threads)指当前正在执行任务的线程数量,它是动态变化的,和线程池的总线程数不是同一概念。
二、活跃线程数下降的核心原因
结合服务器配置(共56个逻辑CPU核心)和日志数据,主要原因集中在以下两点:
- 任务执行出现阻塞瓶颈
初期任务可能是纯计算或阻塞时间极短的逻辑,700个线程可以同时抢占CPU执行;但随着任务推进,任务开始依赖外部资源(如数据库连接、Redis连接、文件IO等),大量线程阻塞在等待资源的环节,只有少数线程能获取到资源并执行任务,导致活跃线程数骤降。从日志看,内层任务队列持续增长(从78k到500k)、完成任务量巨大(4.5M),说明任务一直在提交,但多数线程处于等待状态。 - CPU调度的自然限制
服务器只有56个逻辑核心,即使是CPU密集型任务,同时能运行的线程数也不会超过核心数(过多线程会引发频繁切换,操作系统会自动调度最优的运行线程数)。如果后续任务转为CPU密集型,活跃线程数会稳定在接近核心数的水平,36的数值可能是因为部分线程仍在处理IO或等待资源。
三、解决活跃线程数过少的具体方案
1. 定位阻塞根源
- 使用
jstack命令或Arthas工具,查看内层线程池的线程状态:如果大量线程处于WAITING或BLOCKED状态,查看对应栈帧,定位是等待锁、数据库连接还是其他资源。 - 统计任务执行时间,拆分CPU耗时与IO等待耗时,明确任务是CPU密集还是IO密集型。
2. 优化依赖资源
- 如果是外部资源瓶颈(如数据库连接池过小),扩容对应资源池(比如增大数据库连接池最大连接数),或优化资源使用逻辑(缩短连接占用时间、复用连接)。
- 如果是锁竞争问题,优化锁粒度(使用更细粒度的锁、无锁结构),减少线程阻塞时间。
3. 调整线程池配置
- 若任务为IO密集型,当前700线程数的配置本身合理,但需确保线程能获取足够外部资源执行任务;若为CPU密集型,建议将内层线程池的核心/最大线程数调整为CPU逻辑核心数的1~2倍(如56或112),避免过多线程引发切换开销。
- 可考虑使用有界队列并设置拒绝策略,避免任务无限制堆积导致内存溢出。
4. 优化任务提交逻辑
- 外层线程池仅10个线程,当前任务提交速率足够(队列持续增长),但若后续需要更高提交速率,可适当调整外层线程数,注意不要引发新的资源竞争。
内容的提问来源于stack exchange,提问作者chenfang
相关产品推荐
相关产品推荐

