CachedThreadPool因maximumPoolSize=Integer.MAX_VALUE且SynchronousQueue无缓冲,瞬时高并发导致线程数暴增、CPU忙于调度而非执行,应改用有界队列、设线程数上限并配CallerRunsPolicy限流。

为什么CachedThreadPool会让CPU突然飙高
CachedThreadPool不是“扛不住压力”,而是根本没设防——它默认允许创建多达21亿个线程(maximumPoolSize = Integer.MAX_VALUE),且每个新任务都不排队、不等待、不缓冲,直接触发新建线程。一旦瞬时流量激增,成百上千线程在几毫秒内被拉起,CPU立刻陷入高频调度:线程创建/销毁、上下文切换、栈内存分配全在争抢资源,真实干活的线程反而被挤占,响应延迟飙升,监控看到的却是“CPU使用率95%+”。
线程爆炸的真实发生路径
假设一次用户操作提交3个异步日志任务,QPS从100冲到3000:
- 每秒涌入 3000 × 3 = 9000 个任务
- SynchronousQueue 拒绝缓存 → 每个任务找不到空闲线程就立即新建线程
- 1秒内线程数从几十暴涨到2000+,JVM栈内存瞬时吃掉2GB+
- 操作系统线程句柄耗尽(
/proc/sys/kernel/threads-max触顶),开始报fork: cannot allocate memory - CPU大量时间花在调度和锁竞争上,而非执行业务逻辑
别被“60秒回收”骗了
keepAliveTime=60秒看似有兜底,但实际失效:
- 线程只在完全空闲满60秒后才销毁;高峰中总有任务进来,线程永远“忙中带闲”,根本等不到回收时机
- 线程数剧烈波动(比如1分钟内从80→2800→450→2100)引发调度风暴,CPU负载曲线锯齿状抖动
- 运维看到“线程数回落”就放松警惕,其实系统已在OOM边缘反复震荡
怎么改才真正稳住CPU
停用 Executors.newCachedThreadPool(),手动构建可控线程池:
立即学习“Java免费学习笔记(深入)”;
- 用 ArrayBlockingQueue(500) 替代 SynchronousQueue:任务排队,不盲目扩线程
- corePoolSize = CPU核数 × 2(IO密集型),maximumPoolSize ≤ 200(硬上限,绝不留Integer.MAX_VALUE)
- 拒绝策略选 CallerRunsPolicy:让调用方自己执行,自然限流,避免任务丢失又不压垮线程池
- 加基础监控:每10秒记录
getActiveCount()、getQueueSize(),超阈值立刻告警


















