线程池ThreadPoolExecutor源码分析(JDK 17)

313 阅读9分钟

前言

本文深度剖析JDK 17线程池(ThreadPoolExecutor)源码实现,揭示其高效处理百万级并发任务的核心设计。通过解析ctl原子状态控制、Worker执行单元双角色机制、三级缓冲任务调度流程(核心线程→队列→非核心线程)及优雅关闭状态机,展现Java并发大师Doug Lea的工程智慧。结合时序图与设计模式分析,深入探讨线程复用、动态扩缩容和资源管控等关键技术,并指出ThreadLocal污染等典型陷阱的解决方案,为构建高并发系统提供底层原理支撑。

一、核心设计思想

JDK线程池(java.util.concurrent.ThreadPoolExecutor)基于生产者-消费者模型实现,核心设计原则:

  1. 线程复用:避免频繁创建/销毁线程的开销
  2. 资源管控:通过核心/最大线程数控制并发资源
  3. 任务排队:阻塞队列缓冲任务请求
  4. 拒绝策略:过载保护机制

值得借鉴的设计及意图推断

  • 状态压缩设计:将状态和线程数合并为单个原子整型(ctl),避免多字段更新的竞态条件
  • Worker封装:将线程和任务绑定,实现任务执行单元和锁控制的统一
  • 钩子方法:提供beforeExecute/afterExecute扩展点,支持监控等横切关注点

二、核心数据结构

1. 状态控制字段 ctl

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  • 32位整型:高3位存储线程池状态,低29位存储工作线程数
  • 状态定义
    private static final int RUNNING    = -1 << COUNT_BITS; // 111
    private static final int SHUTDOWN   =  0 << COUNT_BITS; // 000
    private static final int STOP       =  1 << COUNT_BITS; // 001
    private static final int TIDYING    =  2 << COUNT_BITS; // 010
    private static final int TERMINATED =  3 << COUNT_BITS; // 011
    

设计模式:状态模式(State Pattern)通过状态流转实现生命周期管理

2. 工作线程容器

private final HashSet<Worker> workers = new HashSet<>();
  • Worker:核心工作单元,封装线程和任务
  • 线程安全:所有访问通过ReentrantLock同步
    private final ReentrantLock mainLock = new ReentrantLock();
    

三、任务执行流程分析

1. execute(Runnable command)源码分析

public void execute(Runnable command) {
    // 步骤1:空任务检查
    if (command == null)
        throw new NullPointerException();
    
    // 获取当前控制状态(包含线程池状态+工作线程数)
    int c = ctl.get();
    
    // 步骤2:尝试创建核心线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))  // true表示创建核心线程
            return;                    // 创建成功直接返回
        c = ctl.get();                 // 创建失败重新获取状态
    }
    
    // 步骤3:尝试任务入队
    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); // 创建无初始任务的非核心线程
    }
    
    // 步骤4:尝试创建非核心线程
    else if (!addWorker(command, false))  // false表示非核心线程
        // 步骤5:执行拒绝策略
        reject(command);
}

2. 执行步骤

  1. 核心线程处理:当新任务提交时,线程池优先检查当前工作线程数是否小于corePoolSize。若是,则立即创建新工作线程处理该任务。

  2. 任务入队:如果核心线程已满,线程池尝试将任务放入阻塞队列(如LinkedBlockingQueue)。入队成功后,会重新获取线程池状态控制变量,进行线程池状态和线程数量检查:

    2.1 线程池已经关闭(可能其他地方执行了 shutdown()shutdownNow()),则移除任务并执行拒绝策略(防止关闭后继续接收任务)。

    2.2 如果线程池为RUNNING状态,则检查线程池中线程数量是否为0,如果为0,则创建无初始任务的非核心线程(allowCoreThreadTimeOut控制是否允许核心线程超时回收,防止所有线程终止导致任务积压)。

  3. 非核心线程处理:当队列已满且工作线程数小于maximumPoolSize时,创建非核心线程处理任务。

  4. 拒绝策略:当队列满且线程数达到maximumPoolSize后,新提交的任务将触发拒绝策略(如抛出RejectedExecutionException)。

3. 执行时序图:

sequenceDiagram
    participant Caller as 调用线程
    participant TPE as ThreadPoolExecutor
    participant Queue as 阻塞队列
    participant Worker as 工作线程
    
    Caller->>TPE: execute(task)
    alt 核心线程未满
        TPE->>Worker: addWorker(task, true)
        Worker-->>TPE: true
        TPE-->>Caller: return
    else 队列未满
        TPE->>Queue: offer(task)
        alt 无工作线程
            TPE->>Worker: addWorker(null, false)
        end
    else 可创建非核心线程
        TPE->>Worker: addWorker(task, false)
    else 拒绝策略
        TPE->>TPE: reject(task)
    end

四、创建线程源码分析

1. 方法签名

private boolean addWorker(Runnable firstTask, boolean core)
  • firstTask:线程执行的第一个任务(可为null)
  • core:true表示核心线程,false表示非核心线程
  • 返回值:线程创建是否成功

2. 状态检查与循环重试

retry:
for (int c = ctl.get();;) {
    // 状态合法性验证
    if (runStateAtLeast(c, SHUTDOWN) && 
        (runStateAtLeast(c, STOP) ||   // 条件1:STOP状态禁止创建
         firstTask != null ||           // 条件2:SHUTDOWN状态禁止新任务
         workQueue.isEmpty()))          // 条件3:队列空时禁止创建无任务线程
        return false;
    
    for (;;) {
        // 容量限制检查
        int wc = workerCountOf(c);
        if (workerCountOf(c)
            >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK)) // 核心/非核心限制
            return false;
        
        // 无锁化线程计数更新
        if (compareAndIncrementWorkerCount(c)) // CAS更新线程计数
            break retry;                      // 成功则跳出
        
        // 状态变化感知
        c = ctl.get();  
        if (runStateAtLeast(c, SHUTDOWN))
            continue retry;  // 状态变化时重试外层检查
    }
}

关键设计

  • 双重循环:外层处理状态变更,内层处理线程计数
  • CAS原子操作:确保线程计数增加的安全性
  • 状态优先原则:先检查线程池状态,再处理线程创建

2. Worker对象创建与启动

Worker w = null;
boolean workerStarted = false;
boolean workerAdded = false;
try {
    // Worker实例化
    w = new Worker(firstTask);  // 封装任务和线程
    
    final Thread t = w.thread;
    if (t != null) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();  // 获取全局锁
        try {
            // 二次状态校验
            int c = ctl.get();
            if (isRunning(c) || 
                (runStateLessThan(c, STOP) && firstTask == null)) {
                
                // 线程状态验证
                if (t.getState() != Thread.State.NEW)
                    throw new IllegalThreadStateException();
                
                // 安全加入集合
                workers.add(w);
                workerAdded = true;
                int s = workers.size();
                if (s > largestPoolSize)
                    largestPoolSize = s;
            }
        } finally {
            mainLock.unlock();
        }
        
        // 锁外启动线程
        if (workerAdded) {
            t.start();  // 不在锁内启动!
            workerStarted = true;
        }
    }
} finally {
    // 异常回滚机制
    if (!workerStarted)
        addWorkerFailed(w);
}

关键设计

  • 锁保护mainLock保护workers集合操作
  • 二次状态检查:防止启动前状态变化
  • 异常回滚addWorkerFailed处理创建失败

五、Worker核心实现

1. Worker类结构

private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final Thread thread;      // 实际执行线程
    Runnable firstTask;       // 初始任务
    
    Worker(Runnable firstTask) {
        setState(-1);        // 禁止中断直到runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    
    public void run() {
        runWorker(this);     // 委托给线程池方法
    }
    
    // 实现简单的不可重入锁
    // The value 0 represents the unlocked state.
    // The value 1 represents the locked state.
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }

    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }
}

2. 任务执行循环 runWorker()

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // 允许中断
    
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) {
            w.lock();   // 获取Worker锁
            
            // 中断处理逻辑...
            
            try {
                beforeExecute(wt, task); // 钩子方法
                try {
                    task.run();          // 执行任务
                    afterExecute(task, null); // 钩子方法
                } catch (Throwable ex) {
                    afterExecute(task, ex);  // 异常处理
                    throw ex;
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly); // 清理工作线程
    }
}

设计模式:模板方法模式(Template Method)通过beforeExecute/afterExecute提供扩展点

六、任务获取机制 getTask()

private Runnable getTask() {
    boolean timedOut = false; // 上次poll是否超时

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 检查状态:SHUTDOWN+空队列 或 STOP状态
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);
        
        // 是否允许超时(核心线程可超时 或 线程数>corePoolSize)
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // 超时获取
                workQueue.take();                                     // 阻塞获取
            if (r != null)
                return r;
            timedOut = true; // 获取超时
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

设计意图:通过超时机制实现空闲线程回收,动态调整线程池大小

七、线程回收机制

1. 超时回收

  • getTask()返回null时触发线程退出
  • keepAliveTime:非核心线程空闲存活时间
  • allowCoreThreadTimeOut:核心线程是否允许超时

2. 异常回收

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly) // 异常退出
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        workers.remove(w); // 从集合移除
    } finally {
        mainLock.unlock();
    }

    tryTerminate(); // 尝试终止线程池

    // 维持最小线程数
    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return;
        }
        addWorker(null, false); // 补充新Worker
    }
}

设计意图:确保线程池具备自愈能力,在异常退出时自动补充新线程

八、关闭流程分析

1. shutdown() 平滑关闭

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        advanceRunState(SHUTDOWN); // 更新状态
        interruptIdleWorkers();    // 中断空闲线程
        onShutdown();              // 钩子方法
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

2. shutdownNow() 立即关闭

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        advanceRunState(STOP);   // 强制STOP状态
        interruptWorkers();       // 中断所有工作线程
        tasks = drainQueue();     // 排出队列任务
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

设计意图:提供两种关闭模式,满足不同场景的关闭需求

九、拒绝策略实现

内置四种拒绝策略均实现RejectedExecutionHandler

// 1. 默认策略:抛出异常
public static class AbortPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException();
    }
}

// 2. 调用者运行策略
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run(); // 在调用线程执行
        }
    }
}

// 3. 丢弃策略
public static class DiscardPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
}

// 4. 丢弃最老策略
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll(); // 丢弃队列头任务
            e.execute(r);        // 重试执行
        }
    }
}

设计模式:策略模式(Strategy Pattern)允许灵活替换拒绝策略

十、关键设计亮点

  1. 状态压缩设计

    • 32位ctl字段同时编码状态和线程数
    • 避免多字段更新的原子性问题
  2. Worker双角色设计

    • 既是任务执行单元(Runnable)
    • 又是同步控制单元(继承AQS)
  3. 优雅关闭机制

    • SHUTDOWN模式:继续处理队列任务
    • STOP模式:立即中断所有线程
  4. 动态线程调整

    • 根据负载自动扩缩容
    • 核心线程可配置超时
  5. 异常处理机制

    • 任务异常通过afterExecute暴露
    • 工作线程异常自动补充新线程

十一、线程池使用陷阱和扩展

1. ThreadLocal污染问题

问题现象

// 用户身份上下文
static ThreadLocal<User> currentUser = new ThreadLocal<>();

executor.execute(() -> {
    currentUser.set(loadUser()); // 任务1设置用户A
    doSomeWork();
    // 未清理ThreadLocal
});

executor.execute(() -> {
    // 可能读取到用户A的数据!
    User user = currentUser.get(); 
});

解决方案

executor.execute(() -> {
    try {
        currentUser.set(loadUser());
        doSomeWork();
    } finally {
        currentUser.remove(); // 强制清理
    }
});

2. 死锁陷阱

// 同一线程池提交相互依赖任务
executor.submit(taskA); // taskA等待taskB结果
executor.submit(taskB); // taskB等待taskA结果

解决方案:使用不同线程池或ForkJoinPool

3. 资源泄漏陷阱

ExecutorService pool = Executors.newCachedThreadPool();
pool.execute(() -> { ... });
// 忘记shutdown()导致线程无法回收

解决方案

try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
    pool.execute(task);
} // 自动关闭(JDK21+)

4. 异常吞噬陷阱

executor.execute(() -> {
    throw new RuntimeException("被吞没!");
});
// 主线程无法捕获异常

解决方案

// 方案1:重写afterExecute
protected void afterExecute(Runnable r, Throwable t) {
    if (t != null) logger.error("Uncaught exception", t);
}

// 方案2:使用Future
Future<?> future = executor.submit(task);
try {
    future.get();
} catch (ExecutionException e) {
    handle(e.getCause());
}

5. 监控扩展:

public class MonitorThreadPool extends ThreadPoolExecutor {

     // 任务开始/结束监控
     protected void beforeExecute(Thread t, Runnable r) {
         monitor.recordStart(r, t);
     }

     protected void afterExecute(Runnable r, Throwable t) {
         monitor.recordEnd(r, t);
     }

     // 线程创建/销毁监控
     protected void terminated() {
         monitor.poolShutdown();
     }
 }

总结

Java线程池的执行流程本质上是生产者-消费者模型的优化实现,其核心设计思想是通过三级缓冲实现资源弹性分配。