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

前言

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

一、核心设计思想

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

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

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

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

二、核心数据结构

1. 状态控制字段 ctl

java 复制代码
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  • 32位整型:高3位存储线程池状态,低29位存储工作线程数

  • 状态定义

    java 复制代码
    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. 工作线程容器

java 复制代码
private final HashSet<Worker> workers = new HashSet<>();
  • Worker:核心工作单元,封装线程和任务

  • 线程安全 :所有访问通过ReentrantLock同步

    java 复制代码
    private final ReentrantLock mainLock = new ReentrantLock();

三、任务执行流程分析

1. execute(Runnable command) 方法

java 复制代码
public void execute(Runnable command) {
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))  // 1. 创建核心线程
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (!isRunning(recheck) && remove(command)) // 2. 加入队列
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);   // 3. 创建非核心线程
    }
    else if (!addWorker(command, false)) // 4. 创建非核心线程
        reject(command);              // 5. 拒绝策略
}

执行时序图

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

设计意图:通过三级缓冲(核心线程→队列→非核心线程)实现资源弹性分配

四、Worker核心实现

1. Worker类结构

java 复制代码
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()

java 复制代码
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()

java 复制代码
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. 异常回收

java 复制代码
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() 平滑关闭

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

2. shutdownNow() 立即关闭

java 复制代码
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

java 复制代码
// 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污染问题

问题现象

java 复制代码
// 用户身份上下文
static ThreadLocal<User> currentUser = new ThreadLocal<>();

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

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

解决方案

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

2. 死锁陷阱

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

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

3. 资源泄漏陷阱

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

解决方案

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

4. 异常吞噬陷阱

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

解决方案

java 复制代码
// 方案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());
}

十一、最佳实践建议

  1. 队列选择

    • 短任务:SynchronousQueue(直接传递)
    • 长任务:LinkedBlockingQueue(无界缓冲)
    • 优先级任务:PriorityBlockingQueue
  2. 参数配置示例

    java 复制代码
    new ThreadPoolExecutor(
        Runtime.getRuntime().availableProcessors(), // corePoolSize
        Runtime.getRuntime().availableProcessors() * 2, // maxPoolSize
        60L, TimeUnit.SECONDS, // keepAliveTime
        new LinkedBlockingQueue<>(1000), // 有界队列
        new CustomThreadFactory(), 
        new CustomRejectPolicy());
  3. 监控扩展

    java 复制代码
    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线程池的执行流程本质上是生产者-消费者模型的优化实现,其核心设计思想是通过三级缓冲实现资源弹性分配:

graph TD A[提交任务] --> B{核心线程是否可用} B -->|是| C[创建核心线程执行] B -->|否| D{队列是否未满} D -->|是| E[任务入队等待] D -->|否| F{非核心线程是否可用} F -->|是| G[创建非核心线程执行] F -->|否| H[触发拒绝策略]
  1. 核心线程处理 :当新任务提交时,线程池优先检查当前工作线程数是否小于corePoolSize。若是,则立即创建新工作线程处理该任务。
  2. 任务入队 :如果核心线程已满,线程池尝试将任务放入阻塞队列(如LinkedBlockingQueue)。入队成功后会二次检查线程池状态。
  3. 非核心线程处理 :当队列已满且工作线程数小于maximumPoolSize时,创建非核心线程处理任务。
  4. 拒绝策略 :当队列满且线程数达到maximumPoolSize后,新提交的任务将触发拒绝策略(如抛出RejectedExecutionException)。

工作线程通过循环从队列获取任务执行,空闲超时(keepAliveTime)的非核心线程会被自动回收

相关推荐
洛阳泰山17 分钟前
基于 Easy Rules 的电商订单智能决策系统:构建可扩展的业务规则引擎实践
java·开发语言·规则引擎·easy rules
THXW.23 分钟前
【Java项目与数据库、Maven的关系详解】
java·数据库·maven
架构师沉默27 分钟前
外卖平台每天1000万订单查询,是如何扛住高并发的?
java·后端·架构
kushu71 小时前
Java 包
java·开发语言
bug菌1 小时前
🤔领导突然考我Spring中的注解@Bean,它是做什么用的?我...
java·后端·spring
JavaArchJourney2 小时前
ArrayList 源码分析
java
寒士obj2 小时前
熟悉并使用Spring框架 - 注解篇
java·spring
BricheersZ2 小时前
LangChain4J-(1)-Hello World
java·人工智能·langchain
回家路上绕了弯2 小时前
Spring ApplicationContext 源码深度剖析:容器的核心引擎
java·spring