一句话总结
ThreadPoolExecutor 有 7 个核心参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。执行流程:新任务来 → 核心线程未满则创建核心线程 → 核心线程满则放入队列 → 队列满则创建临时线程(最多到 maximumPoolSize)→ 线程数达到 maximumPoolSize 且队列满则执行拒绝策略。临时线程空闲超过 keepAliveTime 会被回收。
初级理解
为什么需要线程池?
线程的创建和销毁开销大(涉及系统调用、内存分配)。线程池通过复用线程来:减少创建销毁开销、控制并发数量、统一管理线程。
7 大核心参数
public ThreadPoolExecutor(
int corePoolSize, // ① 核心线程数(常驻线程)
int maximumPoolSize, // ② 最大线程数
long keepAliveTime, // ③ 空闲线程存活时间
TimeUnit unit, // ④ 时间单位
BlockingQueue<Runnable> workQueue, // ⑤ 阻塞队列
ThreadFactory threadFactory, // ⑥ 线程工厂
RejectedExecutionHandler handler // ⑦ 拒绝策略
)
| 参数 | 说明 |
| corePoolSize | 核心线程数,即使空闲也不会被回收(除非 allowCoreThreadTimeOut=true) |
| maximumPoolSize | 最大线程数 = 核心线程 + 临时线程 |
| keepAliveTime | 临时线程空闲超过此时间被回收 |
| workQueue | 任务队列,核心线程满时新任务放入队列 |
| threadFactory | 创建线程的工厂,可自定义线程名、优先级等 |
| handler | 拒绝策略,线程和队列都满时如何处理新任务 |
一句话总结:线程池 = 核心线程 + 临时线程 + 任务队列 + 拒绝策略,通过复用线程提高性能。
中级深入
完整执行流程
// ThreadPoolExecutor.execute() 简化源码
public void execute(Runnable command) {
int c = ctl.get(); // ctl 高3位=状态,低29位=线程数
// 步骤1:当前线程数 < corePoolSize → 创建核心线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) // true=核心线程
return;
c = ctl.get();
}
// 步骤2:核心线程满 → 尝试放入队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 双重检查:如果线程池已关闭,从队列移除并拒绝
if (!isRunning(recheck) && remove(command))
reject(command);
// 如果线程数为0(可能核心线程被回收),补充一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 步骤3:队列满 → 创建临时线程(最多到 maximumPoolSize)
else if (!addWorker(command, false)) // false=非核心线程
// 步骤4:达到 maximumPoolSize → 执行拒绝策略
reject(command);
}
4 种拒绝策略
| 策略 | 行为 | 适用场景 |
| AbortPolicy(默认) | 抛 RejectedExecutionException | 必须感知任务被拒绝 |
| CallerRunsPolicy | 由调用者线程执行任务 | 可承受延迟,不能丢任务 |
| DiscardPolicy | 直接丢弃,不抛异常 | 允许丢任务(如日志) |
| DiscardOldestPolicy | 丢弃队列中最旧的任务,重试 | 优先处理最新任务 |
常见阻塞队列
| 队列 | 特点 | 适用 |
| SynchronousQueue | 不存储任务,直接交给线程 | CachedThreadPool |
| LinkedBlockingQueue | 无界(默认 Integer.MAX_VALUE) | FixedThreadPool |
| ArrayBlockingQueue | 有界,需指定容量 | 推荐生产使用 |
| PriorityBlockingQueue | 按优先级排序 | 需要优先级调度 |
中级要点:执行流程四步走:核心线程 → 队列 → 最大线程 → 拒绝。队列选型很重要,LinkedBlockingQueue 无界可能导致 OOM。
高级拓展
常见线程池类型及风险
// Executors 提供的工厂方法(阿里规范禁止使用!)
// FixedThreadPool — 固定线程数,无界队列 → 可能 OOM
Executors.newFixedThreadPool(10);
// 等价于:new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS,
// new LinkedBlockingQueue<Runnable>());
// CachedThreadPool — 可缓存,线程数无上限 → 可能创建大量线程
Executors.newCachedThreadPool();
// 等价于:new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
// new SynchronousQueue<Runnable>());
// SingleThreadExecutor — 单线程,无界队列 → 可能 OOM
Executors.newSingleThreadExecutor();
// 等价于:new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
// new LinkedBlockingQueue<Runnable>());
// ScheduledThreadPool — 定时任务
Executors.newScheduledThreadPool(5);
线程数如何合理配置?
| 任务类型 | 公式 | 说明 |
| CPU 密集型 | CPU 核数 + 1 | 减少上下文切换 |
| IO 密集型 | CPU 核数 × 2 | IO 等待时可切换其他线程 |
| 混合型 | CPU 核数 × 期望利用率 × (1 + WT/ST) | WT=等待时间,ST=计算时间 |
线程池状态转换
| 状态 | 说明 | 转换 |
| RUNNING | 接受新任务,处理队列中任务 | 初始状态 |
| SHUTDOWN | 不接受新任务,处理队列中剩余任务 | shutdown() |
| STOP | 不接受新任务,不处理队列中任务,中断正在执行的任务 | shutdownNow() |
| TIDYING | 所有任务终止,workerCount=0 | 过渡状态 |
| TERMINATED | 线程池彻底终止 | terminated() 回调后 |
高级加分项:能说出 Executors 工厂方法的 OOM 风险,能根据任务类型给出线程数配置公式,能解释 shutdown/shutdownNow 的区别,知道 ctl 的高 3 位存状态、低 29 位存线程数的设计。
实战场景
场景一:生产环境线程池配置
// 推荐:手动创建,使用有界队列
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程
10, // 最大线程
60L, TimeUnit.SECONDS, // 空闲超时
new ArrayBlockingQueue<>(100), // 有界队列
new ThreadFactoryBuilder().setNameFormat("biz-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
场景二:线程池监控
// 通过 ScheduledExecutorService 定期监控
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
System.out.println("活跃线程: " + executor.getActiveCount());
System.out.println("队列大小: " + executor.getQueue().size());
System.out.println("完成任务: " + executor.getCompletedTaskCount());
System.out.println("线程池大小: " + executor.getPoolSize());
}, 0, 5, TimeUnit.SECONDS);
常见坑
| 坑 | 原因 | 解决方案 |
| 用 Executors 创建 | 无界队列或无限线程 → OOM | 手动 new ThreadPoolExecutor |
| 线程池中 ThreadLocal 不清理 | 线程复用导致值残留 | 任务完成后 remove() |
| shutdown 后不 awaitTermination | 任务未执行完就退出 | shutdown + awaitTermination |
面试模拟
Q:线程池的核心参数和执行流程?
A:7 个参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。执行流程:核心线程未满创建核心线程 → 核心线程满放入队列 → 队列满创建临时线程(最多到 maximumPoolSize)→ 达到最大线程且队列满执行拒绝策略。临时线程空闲超时回收。
Q:为什么阿里禁止用 Executors 创建线程池?
A:FixedThreadPool 和 SingleThreadPool 使用无界 LinkedBlockingQueue,任务堆积会导致 OOM。CachedThreadPool 允许创建无限线程(Integer.MAX_VALUE),也会 OOM。应该手动 new ThreadPoolExecutor 并使用有界队列。
Q:shutdown() 和 shutdownNow() 的区别?
A:shutdown() 将线程池状态设为 SHUTDOWN,不再接受新任务,但会继续处理队列中已有的任务。shutdownNow() 将状态设为 STOP,不再接受新任务,中断正在执行的任务,返回队列中未执行的任务列表。两者都不会等待任务完成,需要配合 awaitTermination()。