线上CPU飙高主因是线程池配置失当而非线程过多;需依任务类型匹配线程数、队列与拒绝策略:CPU密集型设核心线程数为CPU核数+1、禁扩容、用SynchronousQueue;IO密集型宜设核心线程2N~4N、有界队列、CallerRunsPolicy。

线上 CPU 飙高常不是线程太多,而是线程池配置失当导致资源错配——比如用大量线程干 CPU 密集活,或让 IO 等待任务挤占计算资源。关键不是“压低线程数”,而是让线程数量、队列行为和拒绝策略匹配真实任务特征。
CPU 密集型任务:压紧线程数,避免上下文切换
这类任务(如加解密、图像缩放、复杂规则计算)几乎不等待,线程一多就频繁切换,CPU 白耗在调度上。
- 核心线程数设为 Runtime.getRuntime().availableProcessors() + 1,+1 是防止单个线程因缺页等短暂阻塞时 CPU 空转
- 最大线程数与核心一致,禁用弹性扩容——扩了也跑不满,只增调度开销
- 用 SynchronousQueue 或极小容量的 ArrayBlockingQueue(如 16),逼任务直接创建线程执行,不堆积
- keepAliveTime 设为 0,非核心线程不保留;拒绝策略选 AbortPolicy 或 CallerRunsPolicy,让过载压力回传到上游,触发限流或降级
IO 密集型任务:放宽线程数,但必须控队列
网络调用、数据库查询、文件读写等任务大部分时间在等,CPU 有空闲,可并发更多线程,但无界队列会把所有任务缓存起来,内存涨、响应慢、掩盖瓶颈。
- 核心线程数从 2 × CPU 核心数 起步,压测中逐步调高(常见范围是 2N~4N)
- 最大线程数设为 核心数 × 2~3 倍,留出弹性空间,但不宜超过 100(防线程创建风暴)
- 必须用有界队列,如 ArrayBlockingQueue(100) 或 LinkedBlockingQueue(500),容量按平均 QPS × 平均处理时长 × 安全系数估算
- keepAliveTime 设为 30~60 秒,让临时线程在低峰期自然回收
- 拒绝策略优先选 CallerRunsPolicy:调用方线程亲自执行任务,天然限速,避免雪崩
查清真实负载类型,别靠猜
很多服务是混合型(比如先查 DB 再做计算),单靠任务名判断容易误配。得看运行时表现:
立即学习“Java免费学习笔记(深入)”;
- 用 jstack 抓堆栈,统计线程状态:若大量线程处于 RUNNABLE 且堆栈深陷在 compute 方法里,就是 CPU 密集
- 若大量线程卡在 WAITING / TIMED_WAITING,堆栈停在 SocketInputStream、JDBC Driver、OkHttp connect 等位置,就是 IO 密集
- 用 arthas thread -n 5 查 CPU 占用最高的前 5 个线程,看它们实际在干什么
- 监控 activeCount / poolSize 比值:长期 > 0.8 说明线程不够;长期
避开高危默认配置
Executors 工具类封装的线程池在生产环境极易引发 CPU 飙高:
- newFixedThreadPool(n) 底层用的是 LinkedBlockingQueue(无界),任务积压→内存暴涨→GC 频繁→CPU 暴涨
- newCachedThreadPool() 最大线程数 Integer.MAX_VALUE,突发流量下疯狂建线程,瞬间打满 CPU 和内存
- 正确做法:始终用 ThreadPoolExecutor 显式构造,明确指定队列容量和拒绝策略


















