Java并发(三):线程池

113 阅读5分钟

1. 生命周期

  • RUNNING:运行中。能够接受新任务,以及对新添加的任务进行处理
  • SHUTDOWN:已开始关闭。不可以接受新任务,但是可以对已添加的任务进行处理
  • STOP:已停止。不接收新任务,不处理已添加的任务,并且会中断正在处理的任务
  • TIDYING:正在清理资源。执行钩子函数terminated(),默认空实现
  • TERMINATED:已完全关闭

线程池用AtomicInteger类型的ctl字段记录线程池中的任务数量线程池的状态两个信息,共32位,前3位表示线程池状态,后29位表示线程池中的任务数量

2. 构造参数

  • 核心线程数:常驻线程数量,空闲时也存活
  • 最大线程数:允许的最大线程数,只有在工作队列已满且仍有新任务提交时启用
  • 线程工厂:允许自定义线程的创建行为,如命名规则、优先级调度、守护线程、异常处理等,默认使用Executors.defaultThreadFactory()
  • 拒绝策略:RejectedExecutionHandler的实现,当线程池已经关闭、达到最大线程数且等待队列已满时执行
  • 阻塞等待队列:BlockingQueue类型队列
  • 线程存活时间:非核心线程空闲时可以保持存活的时间
  • 时间单位:秒、毫秒等

3. 执行流程

3.1 execute

  1. 首先判断运行线程数,小于核心线程数则尝试启动线程,成功则直接返回
  2. 其次判断线程池状态,在运行中就加入阻塞等待队列。此时有两个特殊检验:
    • 如果线程池状态突然变为不是运行,且该任务已被其他线程执行因此无法从阻塞队列中remove,则执行拒绝策略
    • 如果线程池状态是运行中,但工作线程数为0,则需要启动新线程,之后再从阻塞队列中取出任务执行
  3. 如果线程池状态不在运行中,或加入阻塞等待队列失败,则尝试启动新线程,若也失败,则执行拒绝策略
public void execute(Runnable command) {
    // 如果无指令,则空指针  
    if (command == null)
        throw new NullPointerException();
    // 获取ctl(前3位表示线程池状态,后29位表示运行线程数)
    int c = ctl.get();
    // 如果运行线程数小于核心线程数,则添加Worker尝试启动新线程执行任务,成功则返回,不成功则获取最新的ctl,根据新的线程池状态和等待队列处理任务
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 如果线程池状态在运行中,且等待队列能够加入
    if (isRunning(c) && workQueue.offer(command)) {
        // 加入队列后,再获取ctl
        int recheck = ctl.get();
        // 如果线程池状态不在运行中,则从队列中移除,拒绝
        // PS:如果remove失败,则说明该任务在其他线程执行了,此线程跳过,无需执行
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 如果线程池状态在运行中,工作线程=0,按理应该新建线程执行任务,但任务已经加入等待队列,因此新建空线程
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    } // 如果线程池不在运行中,或等待队列无法加入,则尝试启动新线程,若失败,则拒绝
    else if (!addWorker(command, false))
        reject(command);
}

3.2 addWorker

循环retry,判断是否满足新增Worker的条件:

  1. 首先判断线程池状态是否可用:如果线程池状态已关闭 或 正在关闭且有等待队列有待执行的线程,则直接返回false,代表无法加入新的线程
  2. 其次判断线程数量:如果工作线程大于容量 或 工作线程大于最大线程数,则也直接返回false
  3. 循环CAS(手写自旋锁):通过校验后,则说明可以增加线程任务,使用CAS尝试增加工作线程数ctl,成功则退出循环。失败则重试CAS,如果线程池状态改变,则重试retry

新增Worker:

  1. 加锁,确保线程安全
  2. 获取线程池状态,如果线程池状态在运行中 或 正在关闭中且传入的任务为空,则将任务加入workers线程集合中,并更新最大线程记录到workers集合中,设置workerAdded为true代表成功加入执行列表
  3. 最后新Worker的线程开始执行,设置workerStarted为true,代表启动成功
private boolean addWorker(Runnable firstTask, boolean core) {
    // retry标签定义可重试的代码块,用于在CAS操作失败时重新循环判断条件
    retry:
    for (;;) {
        // 获取线程池状态
        int c = ctl.get();
        int rs = runStateOf(c);
        // 如果线程池状态已关闭 且 (线程池正在关闭 且 无任务 且 等待队列不为空)则直接返回false,表示无法添加新的工作线程
        if (rs >= SHUTDOWN &&
        ! (rs == SHUTDOWN && firstTask == null &&! workQueue.isEmpty()))
            return false;
        for (;;) {
            // 获取当前工作线程数
            int wc = workerCountOf(c);
            // 如果工作线程数大于容量 或 大于核心和最大线程数,则直接返回false,表示无法添加新的工作线程
            if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 使用CAS尝试增加工作线程数,成功则退出循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get(); // Re-read ctl
            // 如果线程池状态变更,则退出当前循环,,重试retry循环
            if (runStateOf(c) != rs)
                continue retry;
        }
    }
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 加锁确保线程安全
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                int rs = runStateOf(ctl.get());
                // 如果线程池在运行中 或 线程池正在关闭且任务为空,则将任务加入workers线程集合中,并更新最大线程记录
                if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 如果线程成功加入workers集合,则开始执行线程
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // 如果线程未成功启动,则将线程从工作线程中移出,更改工作线程数
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}