【JUC-05】JUC—线程池核心类ThreadPoolExecutor源码解析


前言:本节作为JUC框架的第五节,主要介绍线程池核心类 ThreadPoolExecutor的作用,在前一章节中讲解了 ThreadPoolExecutor类的继承关系 【JUC-04】JUC—与线程池有关的Executor框架介绍本节主要来分析线程池的核心实现类,在阅读本文之前最好先了解一下AQS中的作用,因为线程池核心的实现同样是继承于 AQS

1. 为什么要使用线程池

线程的生成和销毁都是需要消耗系统的资源,因此可以提前准备好一堆可以使用的线程、供任务调度的使用。主要有以下几点好处:

  1. 降低资源的消耗。通过重复利用已经创建的线程降低线程创建和线程销毁的资源损耗。
  2. 提高响应速度。当任务达到时,不需要等待线程创建就可以执行
  3. 提供线程的可管理性。线程池本身提供了很多方法供用户监控、设置线程池。

2. 线程池是如何做的

从宏观而言,线程池主要的处理思路如下图:

在这里插入图片描述
该图引自java并发编程艺术
在分析线程池处理过程之前,需要明确明个不同的概念,一个是使用者、一个线程工作者,使用者是面向于使用线程池的开发人员,而线程工作者是线程池内部的一个工作线程,开发人员提交一个任务,由内部的工作线程处理。
线程池接受一个新提交的任务时解决思路如下:

  1. 判断线程池工作中线程是否已经超过核心线程数。如果没有超过,则创建一个工作线程来执行任务;如果正在工作的线程数超过了核心线程数,则进入下一个流程。
  2. 判断线程池的阻塞队列(等待队列)是否已经被占满。如果没有占满,则将任务添加到阻塞队列中;如果已经占满,则进入下一阶段。
  3. 判断线程池工作中线程是否已经超过最大线程数。如果没有超过,则创建一个新的工作线程来执行任务;如果超过了最大线程数,则需要根据设置的拒绝策略来处理新的任务。

3. 核心源码解析

1. 关键字段解释

1. ctl

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

原子性整数的ctl主要为了表示两个含义:

  1. workerCount:表示有效的线程数(指的是允许start但不允许stop的工作线程),当然该值也是一个估计值(可能动态变化),并且框架暂时定了该值的最大值。(低位)
  2. runState:表示线程池状态.(高三位)
/**
 * count_bits位数为29
 */
private static final int COUNT_BITS = Integer.SIZE - 3;

/**
 * workerCount的最大大小为2^29-1(位运算)
 */
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

2. runState 线程池的状态

主要有以下几种状态,各个状态含义如下,

  1. RUNNING: 接收新任务及处理等待队列中的任务
  2. SHUTDOWN: 不接受新任务但是处理等待队列中的任务
  3. STOP: 不接受新任务、不处理等待队列中任务、中断正在处理的线程
  4. TIDYING: 所有的任务已经终止,并且workerCount = 0,线程转化为TIDYING时,将会调用terminated()方法
  5. TERMINATED: terminated()方法已经执行完毕,代表线程已经成功关闭。

针对每个状态的流状态如下图:
线程状态流转

各个状态数据,每个状态都存储在二进制的高位,具体数值如下

/**
 * 将高位1左移29位再取反
 */
private static final int RUNNING    = -1 << COUNT_BITS;
/**
 *  SHUTDOWN 0 * 2^29
 */
private static final int SHUTDOWN   =  0 << COUNT_BITS;
/**
 * STOP  将1左移29位  等效于    1 * 2^29
 */
private static final int STOP       =  1 << COUNT_BITS;
/**
 * TIDYING  2 * 2^29    等效于将10左移29位
 */
private static final int TIDYING    =  2 << COUNT_BITS;
/**
 * TERMINATED   3 * 2^29    等效于将11左移29位
 */
private static final int TERMINATED =  3 << COUNT_BITS;

其中计算各个状态分别用二进制计算来表示:

/**
 * 主要是计算runState、workerCount、及ctl等三个元素
 * @param c
 * @return
 */
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

2. 线程数控制字段

  1. corePoolSize:核心线程数,当工作线程数小于该值时,新来的任务会创建新线程来处理,并且线程会一直存活,不会过期
private volatile int corePoolSize;
  1. maximumPoolSize:线程池最大线程数。当工作中的线程数已经超过了最大线程数时,会默认启用拒绝策略
private volatile int maximumPoolSize;
  1. keepAliveTime:线程空闲之后过期的时间,只有线程数大于corePoolSize时或者开启allowCoreThreadTimeOut参数时,该值才起作用
private volatile long keepAliveTime;
  1. allowCoreThreadTimeOut:是否允许核心线程过期
    默认是否false,此时,核心线程空闲也不会过期;如果是true,核心线程会等keepAliveTime时间,然后自动过期
private volatile boolean allowCoreThreadTimeOut;

其他还有一些变量,在后续的讲解中再加以介绍,防止大家突然接收太多参数,比较困惑。

2. Worker内部类

Worker内部类可以理解为任务的处理线程,该类本身继承于AQS类,内部包含了很多AQS的特性。因此该内部类肯定有很多获取锁、释放锁等方法。
该类组成的worker对象本身是放在hashSet中,这里可能有人会问,为什么在一个做多线程事儿的类中却放的是线程不安全的容器呢?

private final HashSet<Worker> workers = new HashSet<Worker>();

因为该框架设计者提供了一个mainLock锁的工具,线程在访问hashset集合之前必须首先获取锁,设计者**(也就是Doug Lea)**也是综合考虑线程安全集合和锁的效率,综合对比,选择利用锁保证线程安全。

/**
 * 持有mainLock锁的才允许访问工作线程集合
 * 为什么不用线程安全的集合呢?
 * 经过测试发现通常情况下,利用lock比并发集合性能更佳。
 */
private final ReentrantLock mainLock = new ReentrantLock();

/**
 * Set containing all worker threads in pool. Accessed only when
 * holding mainLock.
 */
/**
 * 用于存储线程池中所有的工作线程、只有得到mainLock锁时才允许访问
 */
private final HashSet<Worker> workers = new HashSet<Worker>();

1. 构造方法

在调用构造方法时,需要把任务传入。通过getThreadFactory().newThread(this)方法来新建一个线程,newThread方法传入的参数是this,
因为Worker本身继承了Runnable接口,也是一个线程。所以一个Worker对象在启动的时候会调用Worker类中的run方法。

Worker(Runnable firstTask) {
    // 初始化状态为RUNNING
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
}

2. 一般方法

1. run()

该方法是Worker线程类的核心方法,本身是调用runWorker()来启动一个线程,该方法主要提供给外部调用者使用。

public void run() {
    runWorker(this);
}
2. isHeldExclusively()

实现AQS中的isHeldExclusively()方法,判断是否被某个线程独占

/**
  * 实现AQS中的isHeldExclusively()方法,判断是否被某个线程独占
  * 0 表示没有锁定状态
  * 1 表示锁定状态
  * @return
  */
 protected boolean isHeldExclusively() {
     return getState() != 0;
 }
3. tryAcquire(int unused)

获取同步状态,该方法实现AQStryAcquire()方法,该方法是可重入的。主要处理思路如下:

  1. 更新state状态,将该值设置为1
  2. 设置当前线程占有同步状态
  3. 如果获取成功返回true,获取失败,返回false
protected boolean tryAcquire(int unused) {
    if (compareAndSetState(0, 1)) {
         setExclusiveOwnerThread(Thread.currentThread());
         return true;
     }
     return false;
 }
4. tryRelease(int unused)

释放同步状态,主要思路如下:

  1. 释放线程独占
  2. 设置线程状态为SHUTDOWN
    这里为什么不用CAS更新state呢?因为只有拥有同步状态时才可以释放,不存在多线程问题
protected boolean tryRelease(int unused) {
     setExclusiveOwnerThread(null);
     setState(0);
     return true;
 }
4. Worker源码及注解
/**
 * Worker继承自AbstractQueuedSynchronizer并且实现了Runnable接口
 * 利用AQS机制,在任务执行的时候控制锁的获取及释放。
 * 这样可以使正在唤醒等待任务的工作线程被中断,而不是中断正在运行的任务。
 * 使用的是简单的非重入互斥锁,而不是重入锁。不希望工作线程调用线程池控制变量方法时重复请求,不希望重复获取锁
 * 为了防止中断,线程开始启动的时候设置为负值
 *
 * AQS中state字段表示线程是否拥有同步锁
 * 1 表示占有
 * 0 表示没有占有
 */
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    /**
     * This class will never be serialized, but we provide a
     * serialVersionUID to suppress a javac warning.
     */
    private static final long serialVersionUID = 6138294804551838833L;

    /**
     * 在调用构造方法时通过ThreadFactory来创建的线程,是用来处理任务的线程。
     */
    final Thread thread;
    /**
     * 初始化运行时的任务
     * */
    Runnable firstTask;
    /** Per-thread task counter */
    /**
     * 每个线程的计时器,用于统计已经完成的任务
     */
    volatile long completedTasks;

    /**
     * 在调用构造方法时,需要把任务传入。
     * 通过getThreadFactory().newThread(this)方法来新建一个线程,newThread方法传入的参数是this,
     * 因为Worker本身继承了Runnable接口,也是一个线程。
     * 所以一个Worker对象在启动的时候会调用Worker类中的run方法。
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        // 初始化状态为RUNNING
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    /**
     * 将内部runWorker()包装成run()给外部使用
     */
    public void run() {
        runWorker(this);
    }

    // Lock methods
    // The value 0 represents the unlocked state.
    // The value 1 represents the locked state.

    /**
     * 实现AQS中的isHeldExclusively()方法,判断是否被某个线程独占
     * 0 表示没有锁定状态
     * 1 表示锁定状态
     * @return
     */
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }

    /**
     * 获取同步状态
     * 该方法实现AQS中tryAcquire()方法,
     * 该方法不允许重入
     * 1. 更新state状态,将该值设置为1。如果失败,则直接返回false
     * 2. 设置当前线程占有同步状态
     * 3. 如果获取成功返回true,获取失败,返回false
     * @param unused
     * @return
     */
    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    /**
     * 释放同步状态
     * 主要操作:
     * 1. 释放线程独占
     * 2. 设置线程状态为SHUTDOWN
     * 这里为什么不用CAS更新state呢?因为只有拥有同步状态时才可以释放,不存在多线程问题
     * @param unused
     * @return
     */
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    /**
     * 下面三个方法直接给ThreadPoolExecutor使用
     * 本身也是调用了AQS底层的acquire()及release()方法
     */
    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()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

3. 构造方法

ThreadPoolExecutor主要提供了四种构造方法,整体功能都差不多,本次只讲解参数最多的一个构造方法,其他的方法只不过是减少了几个参数,使用默认的参数配置。

/**
 * 创建一个线程池,可以通过给定初始化的参数
 * 系统推荐了更方便的{@link Executors}工具类
 * @param corePoolSize      存在线程池中的线程数、如果没有设置allowCoreThreadTimeOut值,即使空闲线程也会保留
 * @param maximumPoolSize   线程池中允许最大工作线程
 * @param keepAliveTime     当实际线程数超过核心线程数时,空闲线程可以存活的最长时间
 * @param unit              keepAliveTime的时间单位
 * @param workQueue         阻塞队列,线程数超过corePoolSize时,请求的线程会先进入阻塞队列中,只有当阻塞队列也满了,才会去创建线程
 * @param threadFactory     创建线程的工厂,所有的线程都是通过该工厂创建
 * @param handler           线程满时或者停止时的拒绝策略
 */
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;
}

主要讲解一下拒绝策略,其中JDK本身提供了四种线程满时的拒绝策略:
分别是

  1. CallerRunsPolicy
/**
 * 拒绝策略:调用者线程来处理被拒绝的任务
 */
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}
  1. AbortPolicy

/**
 * 拒绝策略:直接拒绝,并且往外抛出异常,该策略为JDK默认的拒绝策略
 */
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());
    }
}
  1. DiscardPolicy
/**
 * 拒绝策略:直接废弃被拒绝的任务
 */
public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}
  1. DiscardOldestPolicy
/**
 * 拒绝策略:废弃最早被提交未被处理的请求
 */
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

4. 核心方法讲解

首先根据该类的继承关系一次介绍ThreadPoolExecutor中的核心方法。

1. execute()

为什么第一个首先讲解该方法呢,该方法是处理一个任务的主要方法,该方法主要实现了Executor接口中的execute()方法,该方法其实就是本文头部第二节中的线程池是如何做的。
思路如下:
提交任务,主要分三种情况处理

  1. 创建新线程来处理
  2. 可能会放到已存在的线程池中
  3. 还可能不会提交任务,因为执行器可能中断、或者已经达到了最大的线程池容量,拒绝策略开始生效
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    //clt记录着runState和workerCount
    int c = ctl.get();

    /**
     * workerCountOf方法取出低29位的值,表示当前活动的线程数;
     * 第一种情况:
     * 如果当前活动线程数小于corePoolSize,则新建一个线程放入线程池中;并且把任务放到线程池中
     * 如果添加失败了,则重新获取ctl值
     */
    if (workerCountOf(c) < corePoolSize) {
        /**
         * addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize来判断还是maximumPoolSize来判断;
         * 如果为true,根据corePoolSize来判断;
         * 如果为false,则根据maximumPoolSize来判断
         */
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }

    /**
     * 第二种情况:如果当前线程池是运行状态并且任务可以添加到队列
     * 重新获取ctl值,判断状态,因为有可能在上次检查之后线程再次改变状态
     */
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        /**
         * 再次判断线程池的运行状态,如果不是运行状态,由于之前已经把command添加到workQueue中了,这时需要移除该command
         * 执行过后通过handler使用拒绝策略对该任务进行处理,整个方法返回
         */
        if (! isRunning(recheck) && remove(command))
            reject(command);
        /**
         * 获取线程池中的有效线程数,如果数量是0,则执行addWorker方法
         * 这里传入的参数表示:
         * 1. 第一个参数为null,表示在线程池中创建一个线程,但不去启动;
         * 2. 第二个参数为false,将线程池的有限线程数量的上限设置为maximumPoolSize,添加线程时根据maximumPoolSize来判断;
         * 如果判断workerCount大于0,则直接返回,在workQueue中新增的command会在将来的某个时刻被执行。
         */
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }

    /**
     * 第三种情况:需要执行拒绝策略
     * 如果执行到这里,有两种情况:
     * 1. 线程池已经不是RUNNING状态;
     * 2. 线程池是RUNNING状态,但workerCount >= corePoolSize并且workQueue已满。
     * 再次调用addWorker方法,但第二个参数传入为false,将线程池的有限线程数量的上限设置为maximumPoolSize;
     * 如果失败则拒绝该任务
     */
    else if (!addWorker(command, false))
        reject(command);
}
1.1 addWorker(Runnable firstTask, boolean core)

判断是否开启一个新的线程来处理任务。
firstTask:表示想要添加的任务
core:表示是根据corePoolSize、还是maximumPoolSize判断线程添加的门槛

/**
 * 判断在当前的线程池状态及corePoolSize、maxPoolSize值下,是否新添加一个新的任务
 * 如果添加成功,线程池参数跟随调整,
 * 如果添加失败,表示线程池状态为stop、准备shutdown,或者线程工厂创建线程失败。
 * @param firstTask 第一个开始启动的任务
 * @param core      如果是依据corePoolSize判断则为true,maximumPoolSize判断为false
 * @return          如果插入成功,则返回true,否则返回false
 */
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:

    /**
     * 在下面两层循环中,先循环的获取任务
     */
    //第一层循环
    for (;;) {
        //获取线程的状态
        int c = ctl.get();
        int rs = runStateOf(c);

        /**
         * 该判断条件:
         * 1. 如果rs >= SHUTDOWN,则表示此时不再接收新任务;
         * 2. 接着判断以下3个条件,只要有1个不满足,则返回false:
         *      1. 状态为SHUTDOWN,这时表示关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务
         *      2. firsTask为空
         *      3. 阻塞队列不为空
         * 首先考虑rs == SHUTDOWN的情况
         * 这种情况下不会接受新提交的任务,所以在firstTask不为空的时候会返回false;
         * 然后,如果firstTask为空,并且workQueue也为空,则返回false,
         * 因为队列中已经没有任务了,不需要再添加线程了
         */
        if (rs >= SHUTDOWN && !
                (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
            return false;

        // 第二层循环
        for (;;) {
            // 获取线程数
            int wc = workerCountOf(c);
            /**
             * 如果wc超过CAPACITY,也就是ctl的低29位的最大值(二进制是29个1),方法直接返回false;
             * 这里的core是addWorker方法的第二个参数,如果为true表示根据corePoolSize来比较,如果为false则根据maximumPoolSize来比较。
             */
            if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                return false;

            //尝试增加workerCount,如果成功,则跳出第一个for循环,到最外层逻辑上
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // 如果增加workerCount失败,则重新获取ctl的值
            c = ctl.get();
            // 如果当前的运行状态不等于rs,说明状态已被改变,返回第一个for循环继续执行
            if (runStateOf(c) != rs)
                continue retry;
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 根据firstTask来创建Worker对象,每一个Worker对象都会创建一个线程
        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());

                /**
                 * rs < SHUTDOWN表示是RUNNING状态;
                 * 如果rs是RUNNING状态或者rs是SHUTDOWN状态并且firstTask为null,向线程池中添加线程。
                 * 因为在SHUTDOWN时不会在添加新的任务,但还是会执行workQueue中的任务
                 */
                if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    //workers是一个HashSet,将该worker对象添加其中
                    workers.add(w);
                    int s = workers.size();
                    // largestPoolSize记录着线程池中出现过的最大线程数量
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    // 更新状态
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            /**
             * 如果添加成功,则启动线程
             */
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        // 如果任务启动失败,则调用添加任务失败方法
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}
1.1.1 addWorkerFailed()

刚才说到如果添加工作线程失败,会调动addWorkerFailed()方法,处理添加工作线程失败后的事情,主要目的是从works的hashSet集合中移除已经添加的工作线程。

/**
 * 工作线程创建失败时回退线程
 * 1. 从工作线程hashSet中移除工作线程
 * 2. workCount减1
 * 3. 尝试中断线程
 * @param w
 */
private void addWorkerFailed(Worker w) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            workers.remove(w);
        decrementWorkerCount();
        tryTerminate();
    } finally {
        mainLock.unlock();
    }
}

上面的第二步及第三步主要调用以下方法:

1.1.2 decrementWorkerCount()
/**
 * 将workerCount数减1
 */
private void decrementWorkerCount() {
    do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
1.1.3 tryTerminate()
/**
 * 尝试将线程池状态改成TERMINATED
 * 什么情况下会将状态转成TERMINATED呢?
 *      1. 调用shutDown方法或者线程池为空
 *      2. 调用stop()方法或者线程池为空
 */
final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        /**
         * 当前线程池的状态为以下几种情况时,直接返回:
         * 1. RUNNING,因为还在运行中,不能停止;
         * 2. TIDYING或TERMINATED,因为线程池中已经没有正在运行的线程了;
         * 3. SHUTDOWN并且等待队列非空,这时要执行完workQueue中的task;
         */
        if (isRunning(c) || runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        // 如果线程数量不为0,则中断一个空闲的工作线程,并返回
        if (workerCountOf(c) != 0) {
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        // 这里尝试设置状态为TIDYING,如果设置成功,则调用terminated方法
        try {
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated();
                } finally {
                    // 设置状态为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

2. shutdown()

该方法主要实现了ExecutorService接口中的shutdown(),主要目的是开始有序关闭线程池。在关闭中先执行之前已经提交的任务、但是不接收新任务,如果线程池已经关闭,调用也不会有任何影响。注意:该方法不会等之前已提交的任务执行完成再关闭

/**
 * 思路:
 * 1. 获取锁
 * 2. 检查是否允许关闭
 * 3. 将state状态更新值SHUTDOWN
 * 4. 中断空闲线程
 * 5. 真正的关闭线程
 * 6. 释放锁
 * 7. 尝试将线程池状态改成TERMINATED
 */
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    	// 判断是否允许关闭线程,主要是做安全检查
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

该方法本身也是调用五个方法来实现关闭功能,下面主要分析五个方法

2.1 advanceRunState(int targetState)

将状态更新至目标状态(SHUTDOWN or STOP),主要给shutdown()及shutdownnow()调用

思路:死循环更新状态

  1. 获取当前ctl值
  2. 判断当前ctl是否大于目标状态(targetState),如果是,直接调出循环,否则进入第三步
  3. 尝试将当前状态更新为目标状态,如果成功则退出,不成功,则循环重试。
private void advanceRunState(int targetState) {
    for (;;) {
        int c = ctl.get();
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}
2.2 interruptIdleWorkers()

中断空闲线程,该方法本身是调用interruptIdleWorkers(boolean onlyOne)实现中断空闲线程功能。因此主要分析interruptIdleWorkers(boolean onlyOne)方法。

private void interruptIdleWorkers() {
    interruptIdleWorkers(false);
}

interruptIdleWorkers(boolean onlyOne)方法主要思路:
如果传入的参数onlyOne为true,表示最多中断一个工作线程。
该方法只有tryTerminate()方法中才调用,此时线程中有其他线程能够被终止
思路:
1. 获取mainLock锁
2. 遍历所有工作任务,如果工作任务所在的线程没有被中断,并且获取锁成功,则中断该线程

private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}
2.3 tryTerminate()

尝试将线程池状态改成TERMINATED
什么情况下会将状态转成TERMINATED呢?

  1. 调用shutDown方法或者线程池为空
  2. 调用stop()方法或者线程池为空
final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        /**
         * 当前线程池的状态为以下几种情况时,直接返回:
         * 1. RUNNING,因为还在运行中,不能停止;
         * 2. TIDYING或TERMINATED,因为线程池中已经没有正在运行的线程了;
         * 3. SHUTDOWN并且等待队列非空,这时要执行完workQueue中的task;
         */
        if (isRunning(c) || runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        // 如果线程数量不为0,则中断一个空闲的工作线程,并返回
        if (workerCountOf(c) != 0) {
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        // 这里尝试设置状态为TIDYING,如果设置成功,则调用terminated方法
        try {
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated();
                } finally {
                    // 设置状态为TERMINATED
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

3. shutdownNow()

该方法主要实现了ExecutorService接口中的shutdownnow(),尝试停止所有活跃的正在执行的任务,停止所有等待中的任务。
shutdownNowshutdown方法处理思路差不多,第二步将线程状态更新至STOP状态,而不是SHUTDOWN状态。主要区别在第二步、第三步、并且增加了第四步移除等待队列中所有的线程。
思路如下:

  1. 检查线程是否可以中断
  2. 将工作线程状态更新至STOP
  3. 中断所有的线程,而不是指只中断空闲线程
  4. 删掉等待队列中的所有线程,将其加入到一个新的空list中并返回。
  5. 释放锁
  6. 尝试将线程池状态改成TERMINATED
/**
 * 尝试停止所有活跃的正在执行的任务,停止所有等待中的任务
 * @return 返回所有的处于等待状态的任务,并且这些等待的任务最后会从阻塞队列中移除
 */
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;
}
3.1 interruptWorkers()

中断所有的线程,包括活跃的线程

/**
 * 1. 先获取mainLock锁,然后依次中断工作线程
 */
private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}
/**
 * 如果已经启动,尝试中断
 */
void interruptIfStarted() {
    Thread t;
    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
        try {
            t.interrupt();
        } catch (SecurityException ignore) {
        }
    }
}
3.2 drainQueue()

删掉等待队列中的所有线程,将其加入到一个新的空list中并返回。

/**
 * 一般使用阻塞队列的drainTo()方法,如果失败,则手动移除、添加
 * @return 返回所有删除的工作线程
 */
private List<Runnable> drainQueue() {
    BlockingQueue<Runnable> q = workQueue;
    ArrayList<Runnable> taskList = new ArrayList<Runnable>();
    q.drainTo(taskList);
    if (!q.isEmpty()) {
        for (Runnable r : q.toArray(new Runnable[0])) {
            if (q.remove(r))
                taskList.add(r);
        }
    }
    return taskList;
}

2. awaitTermination()

该方法同样实现了ExecutorService接口中的awaitTermination(),目的是在所有任务执行之前,线程处于等待状态,阻塞线程。

/**
 * 调用shutdown(),让所有任务执行完之前,线程都会阻塞。
 * @param timeout   超时时间
 * @param unit      时间单位
 * @return
 * @throws InterruptedException
 */
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;
            if (nanos <= 0)
                return false;
            nanos = termination.awaitNanos(nanos);
        }
    } finally {
        mainLock.unlock();
    }
}

4.总结

至此,线程池核心类ThreadPoolExecutor的主要用途及核心源码分析都已经结束,可以看到该类主要是实现了AbstractExecutorServiceExecutorServiceExecutor中的关键方法,其实Worker工作线程主要实现AQS中的一些方法,利用AQS中定义的state表示线程是否被使y用,如果想要了解线程池的源码,不建议直接一头扎进ThreadPoolExecutor源码中,而是从AQSExecutor框架多个类,一个一个熟悉,明白彼此之间的逻辑,最终线程池终将会吃透。

5. 参考文章

  1. oracle官方文档
  2. Java并发编程的艺术
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值