线程池面试必问:从 Executors 到 ThreadPoolExecutor,这篇够了
—— 那个让你崩溃的
ThreadPoolExecutor7 个参数,今天彻底搞懂。
你在 Java 面试中被问过线程池吗?面试官冷不丁一句:"讲讲 ThreadPoolExecutor 的 7 个参数。" 你瞬间脑补 —— corePoolSize、maximumPoolSize... 一串名词在脑海中打转,但就是说不清楚它们到底在干什么。
别慌,今天我们不背概念,我们讲故事。
一、ThreadPoolExecutor 的 7 个核心参数:一家公司的运营模型
想象你在经营一家公司,线程池就是你的员工管理系统。7 个参数,就是这家公司的运营规则。
java
public ThreadPoolExecutor(
int corePoolSize, // 1. 核心线程数(正式员工)
int maximumPoolSize, // 2. 最大线程数(正式+临时工上限)
long keepAliveTime, // 3. 临时工空闲多久辞退
TimeUnit unit, // 4. 时间单位
BlockingQueue<Runnable> workQueue, // 5. 任务队列(等候区)
ThreadFactory threadFactory, // 6. 线程工厂(人事部门)
RejectedExecutionHandler handler // 7. 拒绝策略(任务爆满怎么处理)
);
参数拆解,带类比:
-
corePoolSize(核心线程数) —— 正式员工
- 无论任务多少,这些人永远在岗,即使没事做也不会被裁员。
- 类比:公司的正式编制人员。
-
maximumPoolSize(最大线程数) —— 员工上限
- 正式工 + 临时工的总和,公司能承载的最大人力。
- 超过这个数,新任务就只能走拒绝流程。
-
keepAliveTime(空闲存活时间) —— 临时工的保质期
- 临时工闲着超过这个时间,就被辞退。
- 核心线程默认不辞退,除非你显式设置
allowCoreThreadTimeOut(true)。
-
workQueue(任务队列) —— 等候区
- 任务太多时,正式工忙不过来,任务先在这里排队。
- 常用:
LinkedBlockingQueue(无界)、ArrayBlockingQueue(有界)、SynchronousQueue(不存,直接交接)。
-
ThreadFactory(线程工厂) —— 人事部
- 统一给线程起名、设置优先级、处理异常。
- 默认工厂生成名字如
pool-1-thread-1,生产环境强烈建议自定义,方便日志排查。
-
RejectedExecutionHandler(拒绝策略) —— 任务爆满后的处理
- 队列满 + 线程满,新任务怎么办?
- 四种内置策略:AbortPolicy(抛异常)、CallerRunsPolicy(调用者自己跑)、DiscardPolicy(直接丢)、DiscardOldestPolicy(丢最老的)。
二、Executors 四种工具类:封装背后的陷阱
Executors 是个便捷工厂,帮你快速创建常用线程池。但 —— 阿里规范明确禁止生产环境直接使用它。为什么?我们一个个拆解。
1. newFixedThreadPool —— 固定线程池
java
ExecutorService pool = Executors.newFixedThreadPool(5);
底层参数:
- corePoolSize = 5
- maximumPoolSize = 5
- workQueue =
LinkedBlockingQueue(无界)
特点:
- 线程数固定,不会扩容。
- 任务队列无界,理论可以无限排队。
适用场景: 稳定的后台任务、请求量可预测的服务。
风险: 无界队列可能导致任务堆积 OOM。
2. newSingleThreadExecutor —— 单线程池
java
ExecutorService pool = Executors.newSingleThreadExecutor();
底层参数:
- corePoolSize = 1
- maximumPoolSize = 1
- workQueue =
LinkedBlockingQueue(无界)
特点:
- 串行执行,绝对无并发冲突。
- 线程崩溃会自动重建。
适用场景: 需要顺序执行的任务(日志写入、消息顺序处理)。
风险: 同样是无界队列,有 OOM 隐患。
3. newCachedThreadPool —— 缓存线程池
java
ExecutorService pool = Executors.newCachedThreadPool();
底层参数:
- corePoolSize = 0
- maximumPoolSize =
Integer.MAX_VALUE - keepAliveTime = 60秒
- workQueue =
SynchronousQueue(不存储)
特点:
- 来任务就创建线程,上限极大。
- 空闲 60 秒自动回收线程。
适用场景: 大量短生命周期任务、高并发突发请求。
风险: 线程数可能爆炸,耗尽系统资源。
4. newScheduledThreadPool —— 定时任务线程池
java
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
底层参数:
- corePoolSize = 你传入的数
- maximumPoolSize =
Integer.MAX_VALUE - workQueue =
DelayedWorkQueue(延迟队列)
特点:
- 支持延迟执行、周期执行。
- 继承自 ThreadPoolExecutor,参数稍有不同。
适用场景: 定时任务、心跳检测、周期性数据同步。
风险: 最大线程数无限,高并发下同样有风险。
三、一张表看懂四种线程池
表格
| 线程池 | corePoolSize | maximumPoolSize | 队列类型 | 特点 |
|---|---|---|---|---|
| Fixed | 固定值 | 固定值 | LinkedBlockingQueue(无界) | 线程数固定,不扩容 |
| Single | 1 | 1 | LinkedBlockingQueue(无界) | 单线程串行 |
| Cached | 0 | 无限大 | SynchronousQueue | 自动扩容+回收 |
| Scheduled | 固定值 | 无限大 | DelayedWorkQueue | 支持定时/周期任务 |
四、线程池执行任务的完整流程(面试必问)
所有线程池都严格遵循这个 5 步流程:
- 任务进来 → 核心线程未满 → 创建核心线程执行
- 核心线程已满 → 任务进入等待队列排队
- 队列已满 → 创建非核心线程执行
- 总线程数 = 最大线程数 → 触发拒绝策略
- 任务执行完 → 非核心线程空闲超时 → 自动销毁
记住这个流程,面试官再问你"线程池怎么处理任务的",你就可以从容画图解释了。
五、饱和策略:任务满了怎么办?
当队列满 + 线程满,线程池会触发饱和策略。四种内置策略:
-
AbortPolicy(默认) —— 抛异常,阻止程序继续
- 生产不推荐,会直接导致任务丢失 + 程序崩溃
-
CallerRunsPolicy(最实用) —— 调用者自己执行
- 谁提交任务,谁自己跑(让主线程执行)
- 不会丢任务,能自动降低提交速度,生产环境首选!
-
DiscardPolicy —— 直接丢任务,不抛异常
- 生产绝对禁止!无法排查问题
-
DiscardOldestPolicy —— 丢掉队列最老的任务,再尝试提交新任务
- 生产极少用,可能导致关键任务丢失
六、ThreadFactory:必须自定义线程名
默认工厂生成的线程名如 pool-1-thread-1,生产环境无法区分业务,排查问题极难。
推荐写法:
java
ThreadFactory factory = new ThreadFactory() {
private final AtomicInteger num = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("订单-处理线程-" + num.getAndIncrement());
thread.setDaemon(true); // 可选:设置为守护线程
return thread;
}
};
这样在日志中看到线程名,立刻就能定位是哪个业务模块出问题。
七、企业级最佳实践(必背)
-
不用 Executors,手动
new ThreadPoolExecutor -
用有界队列
ArrayBlockingQueue,避免 OOM -
拒绝策略用 CallerRunsPolicy,安全不丢任务
-
必须自定义 ThreadFactory,线程名带业务名称
-
核心线程数配置建议:
- CPU 密集型:核心数 = CPU 核心数
- IO 密集型:核心数 = 2 × CPU 核心数
总结
- 所有线程池底层都是
ThreadPoolExecutor,核心是 7 个参数 - 四种工具类只是封装,生产环境慎用
- 核心逻辑:任务 → 核心线程 → 队列 → 非核心线程 → 拒绝策略
- 线程池设计的终极目标:稳定、可控、不宕机、好排查
下次面试官再问线程池,你可以自信地说:"线程池就像公司员工管理,核心参数就是正式工、临时工、等候区、拒绝策略的运营规则 —— 我从源码到最佳实践都懂。"
希望这篇文章能帮你真正理解线程池,而不是死记硬背。有问题欢迎交流,我们下期再见。