Java 线程池核心源码解析与实战指南
Java 线程池核心源码解析与实战指南
ThreadPoolExecutor 是 Java 并发编程的核心工具,深入理解其原理、掌握最佳实践、避免常见坑点,是构建高性能、稳定系统的关键。
一、线程池核心参数
ThreadPoolExecutor 的构造函数包含 7 个核心参数,每个参数都影响着线程池的行为。
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
1. corePoolSize(核心线程数)
线程池保持的最小线程数,即使空闲也不会被回收(除非设置 allowCoreThreadTimeOut)。
2. maximumPoolSize(最大线程数)
线程池允许创建的最大线程数,包括核心线程和非核心线程。
3. keepAliveTime(空闲存活时间)
非核心线程空闲等待时间,超过该时间后会被回收。
4. workQueue(任务队列)
用于存储等待执行的任务,常用的有:
ArrayBlockingQueue– 有界队列,容量可配置LinkedBlockingQueue– 无界队列,默认容量 Integer.MAX_VALUESynchronousQueue– 同步队列,不存储元素
二、源码深度解析
1. 关键数据结构
// ThreadPoolExecutor 的核心字段
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private final int corePoolSize;
private final int maximumPoolSize;
private final long keepAliveTime;
private final BlockingQueue<Runnable> workQueue;
ctl 字段说明:一个 int 类型变量,高 3 位存储线程池状态(RUNNING、SHUTDOWN、STOP 等),低 29 位存储线程数。
2. 任务提交流程(execute 方法)
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
// 1. 如果线程数小于 corePoolSize,创建新线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) return;
c = ctl.get();
}
// 2. 如果任务能进入队列,尝试添加
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);
}
// 3. 队列已满,尝试创建非核心线程
else if (!addWorker(command, false))
reject(command);
}
3. Worker 线程处理任务
// Worker 是内部类,继承 AbstractQueuedSynchronizer
private final class Worker extends AbstractQueuedSynchronizer
implements Runnable {
final Runnable runTask(Runnable task) {
final Executor executor = this;
try {
task.run();
} finally {
postRunTasks();
}
}
@Override
public void run() {
runTask(runOnce(this));
}
}
三、任务处理完整流程
- 判断核心线程数 – 若 < corePoolSize,创建新线程
- 判断任务队列 – 若能放入队列,则等待执行
- 判断最大线程数 – 若 < maximumPoolSize,创建非核心线程
- 触发拒绝策略 – 若以上都不满足,执行拒绝策略
四、拒绝策略详解
1. AbortPolicy(默认)
抛出 RejectedExecutionException 异常。
2. CallerRunsPolicy
由调用线程直接执行被拒绝的任务。
3. DiscardPolicy
直接丢弃任务,不抛异常。
4. DiscardOldestPolicy
丢弃队列中最老的任务,然后尝试提交当前任务。
五、使用方式示例
1. 基础使用
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, // 核心线程数
20, // 最大线程数
60L, // 空闲存活时间
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 提交任务
for (int i = 0; i < 100; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task: " + taskId + " on " + Thread.currentThread().getName());
});
}
// 关闭线程池
executor.shutdown();
2. 性能调优
| 场景 | 计算公式 |
|---|---|
| CPU 密集型 | N + 1(N 为 CPU 核数) |
| IO 密集型 | 2N 或 N / (1 – 阻塞系数) |
六、最佳实践清单
- 避免使用 Executors 工具类创建线程池(固定队列大小问题)
- 始终设置拒绝策略,不要依赖默认策略
- 使用有界队列,防止 OOM
- 为线程池添加唯一名称,便于监控和调试
- 及时关闭线程池,避免资源泄漏
- 定期监控线程池指标,及时发现异常
七、总结
ThreadPoolExecutor 是 Java 并发编程的核心工具。深入理解其原理、掌握最佳实践、避免常见坑点,是构建高性能、稳定系统的关键。



发表评论