在 Executors 类里面提供了一些静态工厂,生成一些常用的线程池。

  1. newFixedThreadPool:创建固定大小的线程池。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
  2. newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。
  3. newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
  4. newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
  5. newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

五种工厂方法罗列齐全,但这份清单缺了最关键的一页:《阿里巴巴 Java 开发手册》与生产实践都明确禁止直接使用 Executors 创建线程池——因为它们隐藏的默认值正是事故来源。本文把每种工厂的参数拆开、讲清风险、给出替代写法。

工厂方法参数拆解与风险

工厂 core/max 队列 隐藏风险
newFixedThreadPool n / n LinkedBlockingQueue 无界 任务堆积 → OOM
newSingleThreadExecutor 1 / 1 LinkedBlockingQueue 无界 同上 + 串行瓶颈
newCachedThreadPool 0 / Integer.MAX_VALUE SynchronousQueue 任务突增 → 线程爆炸 → OOM/CPU 耗尽
newScheduledThreadPool n / n DelayedWorkQueue 无界 堆积 → OOM
newSingleThreadScheduledExecutor 1 / 1 DelayedWorkQueue 无界 同上

规律一目了然:要么队列无界、要么线程数无界,二者必居其一。无界意味着失去背压——流量异常时资源被无限占用,直到 JVM 崩溃。这就是禁用 Executors 的根本原因,而不是”风格偏好”。

替代写法:显式构造 ThreadPoolExecutor

ThreadPoolExecutor pool = new ThreadPoolExecutor(
8, 16, // core/max:按任务类型与压测定
60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(500), // 有界队列:背压明确
new ThreadFactoryBuilder()
.setNameFormat("biz-pool-%d").build(), // 命名线程:排障利器
new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略:调用者执行降速

每个参数都显式声明,意味着每个风险都被主动决策过:队列容量=可接受的排队上限,max=资源上限,拒绝策略=超限时的业务态度。

各场景的正确选型

  • 负载平稳、任务均匀:fixed 语义(core=max=n)+ 有界队列,n 按 CPU/IO 密集公式与压测确定
  • 短平快、突发小流量:cached 语义(core=0、max 有限值!)+ SynchronousQueue;max 必须设上限,不能学 Executors 用 MAX_VALUE
  • 严格顺序执行:single 语义(core=max=1)+ 有界队列;或业务层排序后批量提交
  • 定时/周期任务:ScheduledThreadPool(core 按需);注意周期任务抛异常会导致后续轮次静默停止(scheduleAtFixedRate 不捕获异常),任务体内必须 try-catch 兜底
  • 分布式环境定时任务:单机 Scheduled 无法保证集群唯一执行,应上调度中心(XXL-Job/Quartz 集群模式)

使用规范清单

  • 线程池按业务隔离:核心链路与非核心(报表、通知)分池,避免慢任务拖垮主链路
  • 命名线程工厂:线程 dump 里一眼定位归属
  • 监控四指标:活跃线程数、队列深度、拒绝次数、任务平均耗时;队列深度是背压最早的信号
  • 关闭生命周期:应用停机时 shutdown + awaitTermination,避免任务丢失
  • 不要用线程池提交互相依赖的任务(父任务等子任务、子任务排队等父任务释放线程)→ 池死锁;必须嵌套时用不同池或 SynchronousQueue 无缓冲设计规避

Executors 工厂是教学工具,ThreadPoolExecutor 显式构造才是生产工具。区别不在 API 复杂度,而在是否把容量与背压的决策权拿回自己手里——拿回来了,线程池才是资源保护器;拿不回来,它就是 OOM 发生器。