Executors 创建的线程池因无界队列、最大线程数失控、拒绝策略缺失和参数黑盒,极易引发 OOM、线程爆炸与系统雪崩;应手动构造 ThreadPoolExecutor,显式配置有界队列、合理线程数、自定义线程工厂及 CallerRunsPolicy 等。

直接说重点:Executors 创建的线程池隐藏关键参数,默认配置极易引发 OOM 或线程爆炸,生产环境扛不住真实流量。
无界队列埋下内存泄漏隐患
FixedThreadPool 和 SingleThreadExecutor 内部用的是 LinkedBlockingQueue(无界),队列容量默认是 Integer.MAX_VALUE。任务一多、处理一慢,任务就全堆在内存里——不是线程爆了,是堆内存先撑爆。比如接口平均耗时 200ms,QPS 达到 500,10 个核心线程根本消费不完,几十秒内队列就能吃掉几百 MB 堆空间。
最大线程数失控导致系统雪崩
CachedThreadPool 的最大线程数设为 Integer.MAX_VALUE,搭配 SynchronousQueue(不存任务),等于“来一个任务就开一个线程”。突发流量下,线程数飙升到上千,CPU 被频繁调度拖垮,GC 压力剧增,最终整个服务卡死或被 OS 杀掉。
拒绝策略缺失,故障时毫无缓冲余地
Executors 创建的线程池统一使用 AbortPolicy(抛异常),一旦队列满或线程达上限,新任务直接被丢弃,调用方收到 RejectedExecutionException。这在高可用系统里是不可接受的——没有降级、没有日志、没有告警,问题静默发生。
参数黑盒,运维和排查完全被动
线程名全是 pool-1-thread-1 这类默认命名;没设置守护线程;无法监控活跃数、队列积压、拒绝次数;扩容缩容全靠猜。线上出问题时,连哪个业务线程池占满内存都定位不到。
正确做法:手动构造 ThreadPoolExecutor
用有界队列控总量,显式设 core/max 线程数,自定义线程工厂加业务前缀,选 CallerRunsPolicy 实现自然限流。例如:
- CPU 密集型:core = CPU 核数 + 1,队列用
SynchronousQueue - IO 密集型:core = CPU 核数 × 2~4,队列大小按 QPS × 平均耗时预估(如 100 QPS × 0.3s ≈ 30)
- 必须配拒绝策略:优先
CallerRunsPolicy,关键链路可自定义记录 + 告警 - 线程工厂建议用
ThreadFactoryBuilder设置名称、是否守护、优先级
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











