线程池卡顿源于任务积压、线程阻塞或资源失控,需排查队列是否爆满、线程是否真在干活、拒绝策略是否失效;监控activeCount和queue.size,用jstack分析线程状态,显式配置有界队列与合理拒绝策略,并按IO密集型业务调参。

线程池卡顿不是“慢”,而是任务积压、线程阻塞或资源失控的信号。排查要直击三个关键点:队列是否爆满、线程是否真在干活、拒绝策略是否形同虚设。
看队列和活跃线程数,确认是否堆积
这是最快速的判断入口。用两行代码就能暴露问题:
- executor.getActiveCount():如果长期接近或等于最大线程数,说明线程全在忙——但得进一步确认是“真忙”还是“假忙”(比如卡在数据库没超时);
- executor.getQueue().size():若持续大于 200 或占队列容量 70% 以上,基本可断定任务已在排队;若用的是无界队列(如 new LinkedBlockingQueue<>()),这个数字可能飙升到几万甚至几十万,内存占用会同步暴涨。
建议把这两个指标接入监控系统,设置阈值告警(例如队列使用率 >80% 持续 1 分钟就触发)。
查线程状态,区分“真忙”和“假堵”
执行 jstack <pid>,重点观察线程堆栈里高频出现的关键词:
立即学习“Java免费学习笔记(深入)”;
- 看到大量线程停在 java.net.SocketInputStream.read、com.mysql.cj.jdbc.ClientPreparedStatement.execute 或类似 IO 调用上 → 是 IO 阻塞,大概率缺超时配置;
- 大量线程卡在 LinkedBlockingQueue.take() 或 ArrayBlockingQueue.offer() → 队列已满或线程池吞吐跟不上,任务进不去也出不来;
- 线程处于 WAITING 或 TIMED_WAITING 状态,但堆栈里没有业务逻辑 → 很可能是锁竞争或线程池自身参数失配(比如核心线程太少、队列太大)。
验拒绝策略,看系统有没有“退路”
很多卡顿源于拒绝策略被绕过或失效:
- 用 Executors.newFixedThreadPool(n) 或 newCachedThreadPool() → 内部默认用无界队列或不设拒绝策略,等于放弃控制权;
- 队列是有界的,但拒绝策略是 AbortPolicy(直接抛异常),而调用方没做重试或降级 → 异常被吞掉,表面安静,实则丢任务;
- 用了 CallerRunsPolicy,但主线程本身就在高负载下处理请求 → 相当于把压力反灌回 Web 容器线程,导致整个接口变慢。
正确做法是:显式构造 ThreadPoolExecutor,队列用有界实现(如 ArrayBlockingQueue(200)),拒绝策略选 AbortPolicy + 上报告警,或自定义策略记录日志并触发熔断。
按业务类型调参,别套公式硬来
所谓“8 核机器配 8 个线程”只适用于纯计算场景。真实服务大多是 IO 密集型:
- DB/HTTP/Redis 类任务:核心线程数建议设为「日常平均并发请求数」,比如监控显示 DB 查询峰值稳定在 40 QPS,那就从 40–60 开始试;
- 队列大小:设为预期峰值并发的 2–3 倍(如峰值 100,并发设 200–300),宁可稍紧,不给无限堆积空间;
- keepAliveTime:设 30–60 秒,再加 allowCoreThreadTimeOut(true),让低谷期自动缩容,避免空转消耗。



















