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_VALUE
  • SynchronousQueue – 同步队列,不存储元素

二、源码深度解析

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));
    }
}

三、任务处理完整流程

  1. 判断核心线程数 – 若 < corePoolSize,创建新线程
  2. 判断任务队列 – 若能放入队列,则等待执行
  3. 判断最大线程数 – 若 < maximumPoolSize,创建非核心线程
  4. 触发拒绝策略 – 若以上都不满足,执行拒绝策略

四、拒绝策略详解

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 – 阻塞系数)

六、最佳实践清单

  1. 避免使用 Executors 工具类创建线程池(固定队列大小问题)
  2. 始终设置拒绝策略,不要依赖默认策略
  3. 使用有界队列,防止 OOM
  4. 为线程池添加唯一名称,便于监控和调试
  5. 及时关闭线程池,避免资源泄漏
  6. 定期监控线程池指标,及时发现异常

七、总结

ThreadPoolExecutor 是 Java 并发编程的核心工具。深入理解其原理、掌握最佳实践、避免常见坑点,是构建高性能、稳定系统的关键。

Java 线程池核心源码解析

#Java #线程池 #ThreadPoolExecutor #并发编程 #JUC #源码分析 #技术教程

标签

发表评论