1. 项目概述:为什么线程池源码是面试必问点
去年帮朋友公司面试Java中级开发时,我连续面了12个候选人,有9个人在回答线程池问题时都停留在ExecutorService接口的简单使用上。当被问到"corePoolSize和maximumPoolSize在源码中如何协同工作"时,只有2个人能说清楚工作队列饱和后的处理逻辑。这让我意识到,很多开发者对线程池的理解还停留在API调用层面。
线程池作为Java并发编程的核心组件,其源码实现涉及线程调度、资源管理、任务队列等关键设计模式。面试官偏爱考察这个点,不仅因为它能区分候选人的真实水平,更因为通过这段代码能考察:
- 对JUC包的设计理念理解
- 对并发编程核心问题的解决能力
- 对Java内存模型的实际应用
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 线程池核心参数源码级解析
2.1 控制线程生命周期的ctl字段
在ThreadPoolExecutor类中,这个AtomicInteger类型的字段堪称精妙设计的典范。它用32位整数同时存储了:
- 高3位:线程池运行状态(RUNNING、SHUTDOWN等)
- 低29位:工作线程数量(workerCount)
java复制private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这种位运算的封装方式带来了两个实际好处:
- 状态变更时只需一次CAS操作,避免多字段更新的线程安全问题
- 判断线程池状态时无需锁,直接位掩码操作即可
实际开发中我曾遇到过一个坑:在SHUTDOWN状态下提交任务会触发拒绝策略,但很多开发者会误判为线程池已停止。正确做法是结合isTerminating()方法判断。
2.2 核心参数在源码中的交互逻辑
当调用execute()方法时,参数的实际作用顺序如下:
- 当前线程数 < corePoolSize → 创建新线程(即使有空闲线程)
- 任务入队成功 → 二次检查线程状态(防御并发修改)
- 队列已满且线程数 < maximumPoolSize → 创建非核心线程
- 触发拒绝策略 → 默认抛出RejectedExecutionException
java复制public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
