【JDK1.8源码阅读】ThreadPoolExecutor线程池源码实现分析整理(九)

文章目录

  • 线程池优点
  • 线程池基础组成部分
  • ThreadPoolExecutor基本定义
    • 线程池状态变量ctl
    • 线程池核心参数
    • 工作队列workQueue
    • ThreadFactory线程创建工厂
    • RejectedExecutionHandler(饱和策略)
    • 线程池运行监控
    • 常见实例
  • 核心运行流程
    • execute 执行任务
    • Worker工作类
    • 关闭线程池
      • shutdown方法实现
        • 1. 检查shutdown权限
        • 2. 设置线程池控制状态为SHUTDOWN
        • 3. 中断空闲worker
        • 4. 调用shutdown钩子函数
        • 5. 尝试终止
      • shutdownNow方法实现
      • 等待线程池关闭
  • 参考

线程池优点

  1. 减少创建销毁线程的开销
  2. 有效控制线程并发量,避免过多的资源竞争,可以对多线程任务并发进行统一分配、调优和监控。
  3. 响应速度更快,当任务达到时,无需等待线程创建

线程池基础组成部分

  1. 线程池管理器,任务调度,创建管理线程
  2. 工作线程,没任务时处于等待状态
  3. 任务接口,任务统一接口,规定了任务入口,首尾和执行状态等
  4. 任务队列:存储缓冲线程

ThreadPoolExecutor基本定义

ThreadPoolExecutor继承关系如下所示:

Executor是顶层接口,内部定义了唯一方法execute(Runnable command),用来表示一个线程执行调度的工具,具体实现如下:

public interface Executor {
    void execute(Runnable command);
}

在Executor基础上,定义了ExecutorService接口,内部定义了多线程操作的丰富工具方法,源码定义如下:

public interface ExecutorService extends Executor {

    /**
     *在之前提交的,需要被执行的任务中,有序的进行关闭操作,并且此时不会再接受新的任务
     * 如果此时所有的任务已经关闭的话,那么就不会起到什么效果,因为已经没有任务可关闭了
     */
    void shutdown();


    /**
     * 企图关闭所有正在执行的任务,并且中断正在等待要执行的任务,返回一个包含正在等待的任务的列表
     */
    List<Runnable> shutdownNow();

    /**
     * 如果线程已经关闭了,就返回true
     */
    boolean isShutdown();

    /**
     * 如果所有的线程任务已经关闭了,就返回true
     */
    boolean isTerminated();


    /**
     * 只有当所有的任务都成功执行,否则会一直处于阻塞状态,只有当一下情况发生时,才会中断阻塞
     * 例如收到一个关闭的请求,或者超时发生、或者当前的线程被中断后
     */
    boolean awaitTermination(long timeout, TimeUnit unit)
            throws InterruptedException;


    /**
     * 提交一个需要返回结果的任务去执行,返回一个有结果的消息体,只有成功执行后,才会返回结果
     */
    <T> Future<T> submit(Callable<T> task);


    /**
     * 只有当任务成功被执行后,才会返回给定的结果
     */
    <T> Future<T> submit(Runnable task, T result);

    /**
     * 执行Runaable任务,调用Future#get()会堵塞直到完成,返回一个空结果
     */
    Future<?> submit(Runnable task);


    /**
     * 提交一批任务,并返回一批任务的结果列表
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
            throws InterruptedException;

    /**
     * 提交一批任务,并返回一批任务的结果列表
     * 存在超时时间,如果任务在超时时间内未完成,会被取消。
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;


    /**
     * 提交一批任务信息,当其中一个成功的执行,没有返回异常的时候,就返回结果
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
            throws InterruptedException, ExecutionException;

    /**
     * 提交一批任务信息,当其中一个成功的执行,没有返回异常的时候,就返回结果
     * 有超时时间,如果没有任务在超时时间前完成,会抛出TimeoutException
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
}

再往上,AbstractExecutorService抽象类实现了ExecutorService接口,提供了一些基础方法实现。

下面开始看ThreadPoolExecutor,ThreadPoolExecutor实现了,AbstractExecutorService抽象类,内部定义了众多的成员属性,相关源码定义如下:

public class ThreadPoolExecutor extends AbstractExecutorService {
    // 线程池的控制状态(用来表示线程池的运行状态(整形的高3位)和运行的worker数量(低29位))
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // 29位的偏移量
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 最大容量(2^29 - 1)
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    // 线程运行状态,总共有5个状态,需要3位来表示(所以偏移量的29 = 32 - 3)
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
    // 阻塞队列
    private final BlockingQueue<Runnable> workQueue;
    // 可重入锁
    private final ReentrantLock mainLock = new ReentrantLock();
    // 存放工作线程集合
    private final HashSet<Worker> workers = new HashSet<Worker>();
    // 终止条件
    private final Condition termination = mainLock.newCondition();
    // 最大线程池容量
    private int largestPoolSize;
    // 已完成任务数量
    private long completedTaskCount;
    // 线程工厂
    private volatile ThreadFactory threadFactory;
    // 拒绝执行处理器
    private volatile RejectedExecutionHandler handler;
    // 线程等待运行时间
    private volatile long keepAliveTime;
    // 是否运行核心线程超时
    private volatile boolean allowCoreThreadTimeOut;
    // 核心池的大小
    private volatile int corePoolSize;
    // 最大线程池大小
    private volatile int maximumPoolSize;
    // 默认拒绝执行处理器
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
}    

线程池状态变量ctl

在成员变量中,有一个非常重要的内部属性ctl,ctl为线程池的控制状态,用来表示线程池的运行状态(整形的高3位)和运行的worker数量(低29位)),其中,线程池的运行状态有如下几种:

/**
* RUNNING    :    接受新任务并且处理已经进入阻塞队列的任务
* SHUTDOWN    :    不接受新任务,但是处理已经进入阻塞队列的任务
* STOP        :    不接受新任务,不处理已经进入阻塞队列的任务并且中断正在运行的任务
* TIDYING    :    所有的任务都已经终止,workerCount为0, 线程转化为TIDYING状态并且调用terminated钩子函数
* TERMINATED:    terminated钩子函数已经运行完成
**/
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

说明:由于有5种状态,最少需要3位表示,所以采用的AtomicInteger的高3位来表示,低29位用来表示worker的数量,即最多表示2^29 - 1。

线程池核心参数

除了核心内部属性ctl外,ThreadPoolExecutor还定义了一部分对外用于线程池调优的核心属性:

  1. corePoolSize:核心线程数,一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;如果没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。
  2. maximumPoolSize:最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。
  3. keepAliveTime:也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。
  4. workQueue:任务队列,存储暂时无法执行的任务,等待空闲线程来执行任务。
  5. threadFactory:线程工厂,用于创建线程。
  6. handler:当线程边界和队列容量已经达到最大时,用于处理阻塞时的程序

以上参数可以通过ThreadPoolExecutor的构造函数来初始化:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

工作队列workQueue

workQueue可以为以下常见的堵塞队列类型,以让线程池具备不同的特性:

  1. ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
  2. LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
  3. SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
  4. priorityBlockingQueue:一个具有优先级的无限阻塞队列。

ThreadFactory线程创建工厂

4)ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。如:

ThreadFactory myThreadFacotry = new ThreadFactoryBuilder().setNameFormat("myTask-%d").build();

RejectedExecutionHandler(饱和策略)

当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。在ThreadPoolExecutor中,定义了以下4种策略:

  1. AbortPolicy:直接抛出异常。
  2. CallerRunsPolicy:只用调用者所在线程来运行任务。
  3. DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  4. DiscardPolicy:不处理,丢弃掉。

相关源码实现如下:

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    
    public CallerRunsPolicy() { }

    /**
     * 先判断如果线程池不是关闭状态,表示可以接受处理新任务
     * 这个时候尝试用当前线程来运行
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

public static class AbortPolicy implements RejectedExecutionHandler {
    
    public AbortPolicy() { }

    /**
     * 直接抛出异常
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}


public static class DiscardPolicy implements RejectedExecutionHandler {
    
    public DiscardPolicy() { }

    /**
     * 不做任何事情,表示直接丢弃
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    
    public DiscardOldestPolicy() { }

    /**
     * 先判断如果线程池不是关闭状态,表示可以接受处理新任务
     * 从线程池工作队列中丢弃最老的队列(先进先出),而后用线程池调度当前任务
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化存储不能处理的任务。

线程池运行监控

如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题时,可以根据线程池的使用状况快速定位问题。可以通过线程池提供的参数进行监控,在监控线程池的时候可以使用以下属性。

  1. taskCount:线程池需要执行的任务数量。
  2. completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount。
  3. largestPoolSize:线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小,则表示线程池曾经满过。
  4. getPoolSize:线程池的线程数量。如果线程池不销毁的话,线程池里的线程不会自动销毁,所以这个大小只增不减。
  5. getActiveCount:获取活动的线程数。

常见实例

我们可以基于Executors工具类来简化以上参数定义,来辅助创建特定用途的线程池:

  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

核心运行流程

execute 执行任务

ThreadPoolExecutor执行execute方法的处理流程如下:

进一步,线程池运行过程涉及到一些核心成员变量,主要使用流程如下:

  1. 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
  2. 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
  3. 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
  4. 当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
  5. 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
  6. 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭

示意图如下所示:

下面分析下源码实现:

public void execute(Runnable command) {
    if (command == null) // 命令为null,抛出异常
        throw new NullPointerException();
    
    // 获取线程池控制状态
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) { // worker数量小于corePoolSize
        // 创建新的线程来执行command
        if (addWorker(command, true)) // 添加worker
            // 成功则返回
            return;
        // 不成功则再次获取线程池控制状态
        c = ctl.get();
    }
    /** 如果一个任务能够成功入队列,在添加一个线程时仍需要进行双重检查(因为在前一次检查后该线程死亡了)
    * 或者当进入到此方法时,线程池已经shutdown了,所以需要再次检查状态,
    * 若有必要,当停止时还需要回滚入队列操作,或者当线程池没有线程时需要创建一个新线程
    */
    if (isRunning(c) && workQueue.offer(command)) { // 线程池处于RUNNING状态,将命令(用户自定义的Runnable对象)添加进workQueue队列
        // 再次检查,获取线程池控制状态
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command)) // 线程池不处于RUNNING状态,将命令从workQueue队列中移除
            // 拒绝执行命令
            reject(command);
        else if (workerCountOf(recheck) == 0) // worker数量等于0
            // 添加worker,必要扩展线程池线程数大于核心线程数
            addWorker(null, false);
    }
    else if (!addWorker(command, false)) // 向队列中添加失败,说明队列已满,尝试添加worker,必要扩展线程池线程数大于核心线程数
        // 添加失败,拒绝执行命令,说明线程池已到最大线程数
        reject(command);
}

// 获取工作的线程数
private static int workerCountOf(int c)  {
    return c & CAPACITY; 
}

// 是否在运行中
private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

// 根据handle执行相关拒绝策略
final void reject(Runnable command) {
    handler.rejectedExecution(command, this);
}

从源码中看到,实际提交任务是在addWorker函数中完成:
此函数可能会完成如下几件任务

  1. 原子性的增加workerCount。
  2. 用户给定的任务封装成为一个worker,并将此worker添加进workers集合中。
  3. 启动worker对应的线程,并启动该线程,运行worker的run方法。
  4. 回滚worker的创建动作,即将worker从workers集合中删除,并原子性的减少workerCount。

具体源码如下:

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) { // 死循环尝试添加任务
        // 有两层循环,外层循环主要检查线程状态。
        // 获取线程池控制状态
        int c = ctl.get();
        // 获取状态,c & ~CAPACITY,即获取高3位
        int rs = runStateOf(c);
        
        // 检查线程池非运行状态或处于正在关闭状态且队列为空,这两种情况直接返回失败
        if (rs >= SHUTDOWN &&            
            ! (rs == SHUTDOWN &&      
               firstTask == null &&        
               ! workQueue.isEmpty()))    
            return false;

        for (;;) {
            // worker数量
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||                                // worker数量大于等于最大容量
                wc >= (core ? corePoolSize : maximumPoolSize))    // worker数量大于等于核心线程池大小或者最大线程池大小
                // 超额返回添加失败
                return false; 
            // cas尝试添加worker
            if (compareAndIncrementWorkerCount(c))          
                // 跳出外层循环,表示添加成功
                break retry;
            // 添加失败,获取线程池控制状态
            c = ctl.get();  
            if (runStateOf(c) != rs) // 二次确认
                // 运行状态发生变化,从外层循环从新开始,重新获取运行状态
                continue retry;
            // 走到这里,说明cas失败,可能有并发线程提交任务修改了workerCount,这里继续进行内层循环
        }
    }

    // worker开始标识
    boolean workerStarted = false;
    // worker被添加标识
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 初始化worker,获取线程工厂初始化一个新线程
        w = new Worker(firstTask);
        // 获取worker对应的线程
        final Thread t = w.thread;
        if (t != null) { // 线程不为null
            // 线程池锁
            final ReentrantLock mainLock = this.mainLock;
            // 获取锁
            mainLock.lock();
            try {
                // 线程池的运行状态
                int rs = runStateOf(ctl.get());
                // 作二次状态检查
                if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { 
                    if (t.isAlive()) // alive状态代表已经启动且未结束, 线程刚添加进来,还未启动就存活,说明存在异常
                        throw new IllegalThreadStateException();
                    // 将worker添加到worker集合
                    workers.add(w);
                    // 获取worker集合的大小
                    int s = workers.size();
                    // 记录线程池曾经达到的最大线程数
                    if (s > largestPoolSize) // 队列大小大于largestPoolSize
                        // 重新设置largestPoolSize
                        largestPoolSize = s;
                    // 设置worker已被添加标识
                    workerAdded = true;
                }
            } finally {
                // 释放锁
                mainLock.unlock();
            }
            if (workerAdded) { // worker被添加
                // 开始执行worker的run方法
                t.start();
                // 设置worker已开始标识
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted) // worker没有开始
            // 添加worker失败,进行回滚
            addWorkerFailed(w);
    }
    return workerStarted;
}

// 创建一个Worker的代码如下
Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    // 根据线程工厂创建线程
    this.thread = getThreadFactory().newThread(this);
}

// 在添加失败后要进行回滚
/**
 * Rolls back the worker thread creation.
 * - removes worker from workers, if present
 * - decrements worker count
 * - rechecks for termination, in case the existence of this
 *   worker was holding up termination
 */
private void addWorkerFailed(Worker w) {
    // 操作workers需要加锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            // 如果存在则移除
            workers.remove(w);
        // 存在并发可能,cas 死循环减少ctl workers数
        decrementWorkerCount();
        // 尝试终止线程
        tryTerminate();
    } finally {
        mainLock.unlock();
    }
}

// 存在并发可能,cas 死循环减少ctl workers数
private void decrementWorkerCount() {
    do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}

从上面的实现中,我们可以看到execute方法并未真正执行我们command的run方法,而是根据需要创建worker线程或将任务放到workerQueue中,由已经创建的worker线程从workerQueue中获取任务执行。下面看看Worker工作线程的定义以及通过源码看它是怎么实现每个任务调度、执行的过程。

Worker工作类

工作线程:线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,还会循环获取工作队列里的任务来执行。

Worker的属性定义如下:

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable {
    // 版本号
    private static final long serialVersionUID = 6138294804551838833L;

    // worker 所对应的线程
    final Thread thread;
    // worker所对应的第一个任务
    Runnable firstTask;
    // 已完成任务数量
    volatile long completedTasks;
}

Worker实现了Runnable接口,下面看看内部方法的整体实现


// Worker的run方法,在线程池execute方法中真正创建子线程执行的方法
public void run() {
    runWorker(this);
}

// 下面是基于AQS的相关实现方法
// 是否被独占,0代表未被独占,1代表被独占
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) {
    // 设置独占线程为null
    setExclusiveOwnerThread(null);
    // 设置状态为0
    setState(0);
    return true;
}
// 获取锁
public void lock()        { acquire(1); }
// 尝试获取锁
public boolean tryLock()  { return tryAcquire(1); }
// 释放锁
public void unlock()      { release(1); }
// 是否被独占
public boolean isLocked() { return isHeldExclusively(); }
// 
void interruptIfStarted() {
    Thread t;
    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { // AQS状态大于等于0并且worker对应的线程不为null并且该线程没有被中断
        try {
            // 中断线程
            t.interrupt();
        } catch (SecurityException ignore) {
        }
    }
}

从上看到,在Wroker中,实现了一个独占式不可重入锁。在核心run方法中,主要调用了ThreadPoolExecutor中定义的runWorker方法:

final void runWorker(Worker w) {
    // 获取当前线程
    Thread wt = Thread.currentThread();
    // 获取w的firstTask
    Runnable task = w.firstTask;
    // 设置w的firstTask为null
    w.firstTask = null;
    // 释放锁(设置state为0,允许中断)
    w.unlock(); 
    boolean completedAbruptly = true;
    try {
        while (task != null || (task = getTask()) != null) { // 任务不为null或者阻塞队列还存在任务
            // 获取锁
            w.lock();
            // 如果worker线程没有被打断,但是检查(包括二次检查)运行状态在STOPPING之后,则冲断worker线程
            if ((runStateAtLeast(ctl.get(), STOP) ||    // 线程池的运行状态至少应该高于STOP
                 (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) &&    // 再次检查,线程池的运行状态至少应该高于STOP
                !wt.isInterrupted())                    // wt线程(当前线程)没有被中断
                wt.interrupt();                            // 中断wt线程(当前线程)
            try {
                // 在执行之前调用钩子函数
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 运行给定的任务,注意这里执行的是run方法,没有另开子线程执行任务。
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    // 执行完后调用钩子函数
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                // 增加给worker完成的任务数量
                w.completedTasks++;
                // 释放锁
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 处理完成后,调用钩子函数
        processWorkerExit(w, completedAbruptly);
    }
}

在循环执行每个任务开始,通过getTask获取要运行的任务:

private Runnable getTask() {
    boolean timedOut = false; // 用于后续标识从工作队列中堵塞获取任务是否超时

    for (;;) { // 无限循环,确保操作成功
        // 获取线程池控制状态
        int c = ctl.get();
        // 运行的状态
        int rs = runStateOf(c);

        // 如果线程池在关闭状态往后,且工作队列为空,说明不再接受任务处理,结束worker处理任务流程
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { // 大于等于SHUTDOWN(表示调用了shutDown)并且(大于等于STOP(调用了shutDownNow)或者worker阻塞队列为空)
            // 减少worker的数量
            decrementWorkerCount();
            // 返回null,不执行任务
            return null;
        }
        // 获取worker数量
        int wc = workerCountOf(c);

        // 是否允许coreThread超时或者workerCount大于核心大小
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; 
        
        // worker数量大于maximumPoolSize,或者从任务队列获取超时并且上面timed为true,同时工作线程数大于1或者worker阻塞队列为空
        // 满足这个条件,说明非核心工作线程到达指定空闲时间,尝试销毁
        if ((wc > maximumPoolSize || (timed && timedOut))     
            && (wc > 1 || workQueue.isEmpty())) {// 在阻塞队列不为空时,需要保证至少有一个wc
            if (compareAndDecrementWorkerCount(c))            // 比较并减少workerCount
                // 减少成功,返回null,不执行任务,该worker会退出
                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. beforeExecute(wt, task):在执行每个任务前调用,默认空实现
  2. afterExecute(task, thrown):在执行每个任务后调用,默认空实现
  3. processWorkerExit(w, completedAbruptly):在worker线程结束前调用,完成清理工作。

下面看看processWorkerExit实现,执行到这一步有两种可能:

  1. 阻塞队列已经为空,即没有任务可以运行了。
  2. 调用了shutDown或shutDownNow函数。

满足上面任一条件后,会执行processWorkerExit完成工作线程退出操作:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly) // 如果被中断,则需要减少workCount,否则这一步已经在前面完成
        decrementWorkerCount();
    // 获取可重入锁,用于修改workers集合
    final ReentrantLock mainLock = this.mainLock;
    // 获取锁
    mainLock.lock();
    try {
        // 将worker完成的任务添加到总的完成任务中
        completedTaskCount += w.completedTasks;
        // 从workers集合中移除该worker
        workers.remove(w);
    } finally {
        // 释放锁
        mainLock.unlock();
    }
    // 尝试终止
    tryTerminate();
    // 获取线程池控制状态
    int c = ctl.get();
    if (runStateLessThan(c, STOP)) { // 小于STOP的运行状态
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty()) // 允许核心超时并且workQueue阻塞队列不为空
                // 确保最少线程至少有一个
                min = 1;
            if (workerCountOf(c) >= min) // workerCount大于等于min,说明当前线程可以安全结束
                // 直接返回
                return; 
        }
        // 添加worker,表示需要保留当前worker,通过新建一个替代当前实现
        addWorker(null, false);
    }
}

关闭线程池

可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。当所有的任务都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。

但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

至于应该调用哪一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow方法。

shutdown方法实现

public void shutdown() {
    // 加锁操作
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 检查shutdown权限
        checkShutdownAccess();
        // 设置线程池控制状态为SHUTDOWN
        advanceRunState(SHUTDOWN);
        // 中断空闲worker
        interruptIdleWorkers();
        // 调用shutdown钩子函数
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    // 尝试终止
    tryTerminate();
}

在整个尝试关闭过程,进行了5步操作:

  1. 检查shutdown权限
  2. 设置线程池控制状态为SHUTDOWN
  3. 中断空闲worker
  4. 调用shutdown钩子函数
  5. 尝试终止

下面依次分析每一步实现

1. 检查shutdown权限

如果存在安全管理器,确保调用方有权限关闭每一个线程

private void checkShutdownAccess() {
    // 获取系统安全管理器
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        // 检查关闭权限
        security.checkPermission(shutdownPerm);
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                // 检查每个线程的访问权限
                security.checkAccess(w.thread);
        } finally {
            mainLock.unlock();
        }
    }
}

2. 设置线程池控制状态为SHUTDOWN

不断自旋cas操作,确保线程池在指定状态之后或者尝试将线程池更新到指定状态成功,在shutdown调用中为SHUTDOWN状态。

private void advanceRunState(int targetState) {
    for (;;) {
        int c = ctl.get();
        // 在指定状态前或者更新到指定状态成功则中断
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

3. 中断空闲worker

private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 遍历所有worker,如果worker已经启动,尝试进行中断
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}

// 调用一下方法来中断启动的工作线程,期间会检查工作线程是否处于工作状态,只终止不处于工作状态的工作线程。
void interruptIfStarted() {
    Thread t;
    // getState() >= 0,说明当前线程没有执行任务,在尝试从任务队列中获取任务,即代表空闲worker
    // 如果线程没有被中断,则尝试中断线程
    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
        try {
            t.interrupt();
        } catch (SecurityException ignore) {
        }
    }
}

4. 调用shutdown钩子函数

在ThreadPoolExecutor默认实现中,这是一个空实现,供子类拓展:

void onShutdown() {
}

5. 尝试终止

final void tryTerminate() {
    for (;;) { // 无限循环,确保操作成功
        // 获取线程池控制状态
        int c = ctl.get();
        // 检查当前线程池的状态和任务调度情况是否满足转化为终止状态,如果满足以下任意条件则不进行终止
        // 1. 线程池的运行状态为RUNNING
        // 2. 线程池的运行状态在TIDYING之前
        // 3. 线程池的运行状态为SHUTDOWN但workQueue队列不为null
        if (isRunning(c) ||  // 线程池的运行状态为RUNNING
            runStateAtLeast(c, TIDYING) ||   // 或者线程池的运行状态最小要大于TIDYING
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))    // 或者线程池的运行状态为SHUTDOWN但workQueue队列不为null
            // 不能终止,直接返回
            return;
        
        if (workerCountOf(c) != 0) { // 线程池仍有继续运行的必要
            // 注意tryTerminate也可能在一个worker线程需要回收的时候调用
            // 这个时候会触发这里的逻辑,仅仅中断一个空闲的worker。
            interruptIdleWorkers(ONLY_ONE);
            return;
        }
        // 获取线程池的锁
        final ReentrantLock mainLock = this.mainLock;
        // 获取锁
        mainLock.lock();
        try {
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { // 比较并设置线程池控制状态为TIDYING
                try {
                    // 终止,钩子函数,
                    terminated();
                } finally {
                    // 设置线程池控制状态为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    // 释放在termination条件上等待的所有线程,这些线程一般都是正在执行awaitTermination函数。
                    termination.signalAll();
                }
                return;
            }
        } finally {
            // 释放锁
            mainLock.unlock();
        }
        // 存在并发操作导致期间cas失败,继续循环操作
    }
}

中断一个空闲线程的操作interruptIdleWorkers实现如下:

 private void interruptIdleWorkers(boolean onlyOne) {
    // 线程池的锁
    final ReentrantLock mainLock = this.mainLock;
    // 获取锁
    mainLock.lock();
    try {
        for (Worker w : workers) { // 遍历workers队列
            // worker对应的线程
            Thread t = w.thread;
            if (!t.isInterrupted() && w.tryLock()) { // 线程未被中断并且成功获得锁
                try {
                    // 中断线程
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    // 释放锁
                    w.unlock();
                }
            }
            if (onlyOne) // 若只中断一个,则跳出循环
                break;
        }
    } finally {
        // 释放锁
        mainLock.unlock();
    }
}

shutdownNow方法实现

前面提到,shutdownNow方法和shutdown方法的主要区别是shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

下面看看shutdownNow的实现:

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        // 更新为STOP状态
        advanceRunState(STOP);
        interruptWorkers();
        // 获取所有工作队列中的线程
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

private List<Runnable> drainQueue() {
    // 缓存本地
    BlockingQueue<Runnable> q = workQueue;
    ArrayList<Runnable> taskList = new ArrayList<Runnable>();
    // 将队列中值,全部移除,并发设置到给定的集合中。
    q.drainTo(taskList);
    // 工作队列不为空,queue为DelayQueue或其他类型队列drainTo操作会移除一部分元素失败
    // 因而需要在下面一个个移除
    if (!q.isEmpty()) {
        for (Runnable r : q.toArray(new Runnable[0])) {
            if (q.remove(r))
                taskList.add(r);
        }
    }
    return taskList;
}

等待线程池关闭

如果执行了shutdonw或shutdownNow操作,线程池不会立即终止,可以通过调用awaitTermination方法尝试堵塞指定时长,直到超时或线程池完成终止

public boolean awaitTermination(long timeout, TimeUnit unit)
    throws InterruptedException {
    // 转换指定单位的时长。
    long nanos = unit.toNanos(timeout);
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (;;) {
            // 判断是终止状态,则返回终止成功
            if (runStateAtLeast(ctl.get(), TERMINATED))
                return true;
            // 传入时间参数无效,返回false
            if (nanos <= 0)
                return false;
            // 条件堵塞,直到被唤醒或到达nanos超时时间
            nanos = termination.awaitNanos(nanos);
        }
    } finally {
        mainLock.unlock();
    }
}

参考

  1. https://www.cnblogs.com/leesf456/p/5585627.html
  2. https://blog.csdn.net/luckyzhoustar/article/details/78282462
  3. https://blog.csdn.net/fwt336/article/details/81530581
  4. Java并发编程的艺术

你可能感兴趣的:(JDK源码,JDK1.8,源码阅读)