线程池的几种方式与使用场景
在 Executors 类里面提供了一些静态工厂,生成一些常用的线程池。
newFixedThreadPool:创建固定大小的线程池。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。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( |
每个参数都显式声明,意味着每个风险都被主动决策过:队列容量=可接受的排队上限,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 发生器。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus











