本文目录

  • 线程池是如何执行任务的?
  • ThreadPoolExecute变量是如何设计的?
  • 如何确定线程池的状态?
  • 如何得到线程池中线程数量?
  • 如何初始化线程池状态、线程初始数量?
  • 开始真正分析线程池执行流程(execute)
  • addWorker
  • 拒绝任务的情况分析(addWorker失败)
  • 添加至工作集合(workers)情况分析
  • 线程启动流程(start)
  • runWorker
  • 彻底搞懂SHUTDOWN与STOP的区别(getTask)
  • 如何回收work对象?(processWorkerExit)
  • 尝试结束线程池(tryTerminate)体现
  • 线程池的关闭(shutdown)
  • 线程池关闭步骤一(advanceRunState)
  • 线程池关闭步骤二(interruptIdleWorkers)
  • shuDownNow如何中断work的?



本文将从为什么使用线程池 ?—> 线程池的使用方式----> 线程池源码逐步分析线程池

为什么使用线程池?

  1. 降低资源开销:通过重复利用已创建的线程降低创建销毁线程带来的开销
  2. 提高响应速度:当有任务到达时,任务可以不需要等待线程创建就可以立即执行
  3. 提高线程的可管理性:使用线程池可以对线程进行统一的监控管理

线程池的常见创建形式有哪几种?

  1. 通过Executors对象创建我们线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
  1. 通过实例化ThreadPoolExecutor创建我们的线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10,
                20,
                1,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
  1. 使用spring 为我们提供的线程池类,我们可以将ThreadPoolTaskExecutor注入到我们的业务代码中,用法和ThreadPoolExecutor一样

通过ThreadPoolExecutor这种方式创建线程池可能稍微有点繁琐,但是对比通过Executors创建的线程池,可控性会提高很多。推荐使用ThreadPoolExecutor()

构建ThreadPoolExecutor参数释义

5. 核心线程数(corePoolSize)

6. 最大线程数(maximumPoolSize)

7. 空闲线程活跃时间(keepAliveTime)

8. 阻塞队列(queue)

9. 饱和策略(policy)

知其然而知其所以然~线程池深入源码分析-手把手debug源码系列_thread

核心线程数~最大线程数~阻塞队列之间的关系图解

public void execute(Runnable command) {
		/**
		* 任务为null直接抛出异常
		*/
        if (command == null)
            throw new NullPointerException();
        /**
        * 获取线程状态、数量。默认线程状态为为运行状态
        */
        int c = ctl.get();
        //如果当前工作线程数量 < 核心线程数量
        if (workerCountOf(c) < corePoolSize) {
        	//将任务添加到works(hashset)中,同时尝试运行当前任务和阻塞队列中的任务
        	//如果线程池中线程数量大于核心线程数那么会拒绝运行此任务
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果线程池状态为运行状态,此时工作线程数量大于核心线程数量,如果此时阻塞队列没有满,那么将任务尝试放入阻塞队列中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //重新检查线程池状态如果此时是非运行状态,移除任务,同时执行
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
            //开启一个work,运行阻塞队列中的任务
            //如果当前线程池数量大于最大线程数量那么添加任务失败
                addWorker(null, false);
        }
        //阻塞队列满了,且再次尝试运行该任务,都不行,执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }
  1. 如果线程数小于核心线程数那么正常执行任务
  2. 如果线程数大于核心线程数且线程池状态为Running,且阻塞队列未满,那么将当前线程添加到阻塞队列。如果此时线程池状态不是运行状态,移除任务执行饱和策略,反之开启一个新的work,从阻塞队列中拿取任务然后运行
  3. 如果阻塞队列已经满了,当前任务添加到阻塞队列失败,执行reject(饱和策略)

创建好的线程池后,我们直接将任务submit()或者execute()就好了。

submit与execute有什么区别?

  • submit用于执行有返回值的任务(CallAble)
  • execute用于执行没有返回值的任务(RunnAble)

好了前言就到这吧,书写如下demo开始debug线程池源码

public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10,
                20,
                1,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        FutureTask<Integer> result = new FutureTask<Integer>(new task2());
        /**
         * execute用于执行没有返回值的任务
         */
        threadPoolExecutor.execute(new task());
        /**
         * submit用于执行有返回值的任务。
         */
        Future<?> submit = threadPoolExecutor.submit(new task2());
        System.out.println(submit.get());
        threadPoolExecutor.shutdown();
    }

    static class task implements Runnable {
        @Override
        public void run() {
            System.out.println("task runnable");
        }
    }

    static class task2 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("task callable");
            return 1;
        }
    }

上述demo运行结果图

知其然而知其所以然~线程池深入源码分析-手把手debug源码系列_多线程_02

直接在 threadPoolExecutor.execute(new task()) 这断点,看线程池是如何执行任务的?

知其然而知其所以然~线程池深入源码分析-手把手debug源码系列_java_03

线程池是如何执行任务的?

public void execute(Runnable command) {
		/**
		* 任务为null直接抛出异常
		*/
        if (command == null)
            throw new NullPointerException();
            
        int c = ctl.get();
        //如果线程数量小于核心线程数,
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        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);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

ThreadPoolExecute变量是如何设计的?

俺看了一眼上述的代码execute()直呼瑟瑟发抖。先别急研究上面的代码,先来分析一下ThreadPoolExecutor类中的各个变量的含义如下图

知其然而知其所以然~线程池深入源码分析-手把手debug源码系列_多线程_04


从上图可知线程池的状态有那几种?

  1. 运行状态:RUNNING = -1 << COUNT_BITS;
  2. 关闭状态:SHUTDOWN = 0 << COUNT_BITS;
    3. 停止状态:STOP = 1 << COUNT_BITS;
    4. 过度状态:TIDYING = 2 << COUNT_BITS;
    5. 结束状态:TERMINATED = 3 << COUNT_BITS;

COUNT_BITS = 32 - 3 = 29

private static final int COUNT_BITS = Integer.SIZE - 3;

-1 的补码为:11111111111111111111111111111111

RUNNING = -1 << COUNT_BITS = -1 << 29 = 11100000000000000000000000000000

所以 RUNNING = 11100000000000000000000000000000 = -536870912
同理可得 SHUTDOWN = 00000000000000000000000000000000 = 0
同理可得 STOP = 00100000000000000000000000000000 = 536870912
同理可得 TIDYING = 01000000000000000000000000000000 = 1073741824
同理可得 TERMINATED = 01100000000000000000000000000000 = 1610612736

小结:观察每个线程状态的高三位都是独一无二的,高三位记录着线程运行状态,而后29位记录着对应状态的线程数(work数量)。(初始状态后29位组成全是0)

如何确定线程池的状态?

runStateOf:确定线程池的状态的方法

COUNT_BITS = 32 - 3 = 29
 private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
 private static int runStateOf(int c)     { return c & ~CAPACITY; }

CAPACITY = 1<<29 -1 = 00011111111111111111111111111111(线程的最大容量)

~CAPACITY = 11100000000000000000000000000000

c & ~CAPACITY 计算出来的结果永远只与 c 的高三位有关,因此通过 c&~CAPACITY 操作可以确定线程池的运行状态

不论是运行状态还是什么别的状态,它们的前三位都是独一无二的,也就是说高三位是记录线程池的运行状态的

如何得到线程池中线程数量?

workerCountOf:可以确定线程池中的线程数量(work对象的数量)

private static int workerCountOf(int c)  { return c & CAPACITY; }
  • CAPACITY = 1<<29 -1 = 00011111111111111111111111111111

由于 CAPACITY 高三位全是0 ,所以c & CAPACITY的结果只与低29位有关。
例如:假设线程数为1 , 1转换成补码为 00000000000000000000000000000001 与 CAPACITY做 & 运算,得到的结果为 1。

如何初始化线程池状态、线程初始数量?

ctlOf:初始化线程池状态、数量的方法

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
 private static int ctlOf(int rs, int wc) { return rs | wc; }

拿ctlOf(RUNNING, 0)的返回值举例 RUNNING|0 = 11100000000000000000000000000000 | 0 = 11100000000000000000000000000000。
即:初始化了线程池状态为RUNNING 且初始线程数量是 0 。

开始真正分析线程池执行流程(execute)

  1. 如果线程数小于核心线程数那么正常执行任务
  2. 如果线程数大于核心线程数且线程池状态为Running,且阻塞队列未满,那么将当前线程添加到阻塞队列。如果此时线程池状态不是运行状态,移除任务执行饱和策略,反之从阻塞队列中拿取任务然后运行
  3. 如果阻塞队列已经满了,当前任务添加到阻塞队列失败,执行reject(饱和策略)
public void execute(Runnable command) {
		/**
		* 任务为null直接抛出异常
		*/
        if (command == null)
            throw new NullPointerException();
        /**
        * 获取线程状态、数量。默认线程状态为为运行状态
        */
        int c = ctl.get();
        //如果当前工作线程数量 < 核心线程数量
        if (workerCountOf(c) < corePoolSize) {
        	//将任务添加到works(hashset)中,同时尝试运行当前任务和阻塞队列中的任务
        	//如果线程池中线程数量大于核心线程数那么会拒绝运行此任务
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果线程池状态为运行状态,此时工作线程数量大于核心线程数量,如果此时阻塞队列没有满,那么将任务尝试放入阻塞队列中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //重新检查线程池状态如果此时是非运行状态,移除任务,同时执行
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
            //开启一个work,运行阻塞队列中的任务
            //如果当前线程池数量大于最大线程数量那么添加任务失败
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

addWorker

addWorker(Runnable firstTask, boolean core) 第二个参数:如果core 为
true,那么表示如果线程数大于核心线程数将会拒绝任务的启动,如果为false,表示如果大于最大线程数,将会拒绝任务的启动。

此方法关系到线程的启动,先过一遍代码注释,下面有总结。

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
//下面判断条件等价于:rs >= SHUTDOWN && rs != SHUTDOWN || rs >= SHUTDOWN && firstTask != null || rs >= SHUTDOWN && workQueue.isEmpty()
            //1.线程池状态不是运行状态,是STOP、TIDYING、TERMINATED中的一种拒绝执行任务
            //2.线程池状态不是运行状态,是其他状态中的一种,任务不为null,将会拒绝任务
            //3.线程池状态不是运行状态,是其他状态中的一种,,任务队列为空,将会拒绝任务
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;
//死循环
            for (;;) {
            //线程数
                int wc = workerCountOf(c);
                //1.线程数超过了最大容量将会拒绝任务
                //2.线程数大于核心线程数,或者大于最大最大线程数,将会拒绝任务
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                 //CAS操作,对work数量加一
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //其他CAS操作失败的线程一直自旋
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
        //将任务包装成一个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());
                    //1.线程池是运行状态,将任务添加至工作集合
                    //2.任务为null且线程池是SHUTDOWN状态,也将任务添加进工作集合
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        //判断线程是否存活
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                            //works为一个hashSet
                        workers.add(w);
                        //工作集合的大小
                        int s = workers.size();
                        if (s > largestPoolSize)
                        //更新最大线程池的大小
                            largestPoolSize = s;
                            //标记任务添加成功
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                //开启任务
                    t.start();
                    //标记任务启动成功
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

拒绝任务的情况分析(addWorker失败)

rs >= SHUTDOWN && rs != SHUTDOWN || rs >= SHUTDOWN && firstTask != null || rs >= SHUTDOWN && workQueue.isEmpty()

上面的代码和下面的代码可以等价替换

if (rs >= SHUTDOWN &&
    ! (rs == SHUTDOWN &&
       firstTask == null &&
       ! workQueue.isEmpty()))
  1. 线程池状态不是运行状态,是STOP、TIDYING、TERMINATED中的一种直接拒绝任务
rs >= SHUTDOWN && rs != SHUTDOWN
  1. 要想进入if分支必然只可能是第一点中的 rs != SHUTDOWN 为 false ,也就是说线程池状态不是运行状态,且任务不为null,将会直接拒绝任务
rs >= SHUTDOWN && firstTask != null
  1. 线程池状态不是运行状态,阻塞队列为空,将会直接拒绝任务
rs >= SHUTDOWN && workQueue.isEmpty()
  1. 线程数超过了最大容量将会直接拒绝任务
  2. 线程数大于核心线程数,或者大于最大最大线程数,也会直接拒绝任务
if (wc >= CAPACITY ||
    wc >= (core ? corePoolSize : maximumPoolSize))
    return false;

添加至工作集合(workers)情况分析

  1. 线程池是运行状态,将任务添加至工作集合
  2. 任务为null且线程池是SHUTDOWN状态,也将任务添加进工作集合
if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null))

添加成功后,将会启动对应Work中的Thread

知其然而知其所以然~线程池深入源码分析-手把手debug源码系列_多线程_05

线程启动流程(start)

运行到 t.start(); 将会来到Work内部的run方法

知其然而知其所以然~线程池深入源码分析-手把手debug源码系列_并发编程_06

runWorker

一直循环如果当前任务不为null且阻塞队列存在任务就一直执行任务,即运行work中的task.run(),里面比较重要的一个点就是

如果线程池正在停止,请确保线程被中断;如果没有,请确保线程不被中断。这需要在第二种情况下重新检查以处理shutdownNow这种情况,同时清除中断。

runStateAtLeast(ctl.get(), STOP) && !wt.isInterrupted()||( (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)) && !wt.isInterrupted() )
  1. 如果线程池状态为STOP、TIDYING、TERMINATED中的状态之一且任务线程没有被中断,将会中断当前任务线程
  2. 如果线程池状态为RUNNING或SHUTDOWN,但是当前线程已经中断,重新检查线程池状态如果为STOP、TIDYING、TERMINATED中的状态之一,将会中断当前任务线程。不论线程是否中断task.run()都会运行

这里设置线程中断的目的就是:无论线程池是处于什么状态,我们在task.run()期间都能获取到合理的线程状态

while (task != null || (task = getTask()) != null) {

}

上面这段代码task == null 出现的情况是 addWork(null,false),才会出现task == null,此时将会直接处理阻塞队列中的任务

runWorker源代码

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    //拿到Work对象中的任务
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
    //task == null 的情况即为addWork(null,false)
    //如果当前任务不为null或者阻塞队列中能获取到任务
        while (task != null || (task = getTask()) != null) {
        //加锁,与shutdown有关
            w.lock();
            //条件可以拆分runStateAtLeast(ctl.get(), STOP) && !wt.isInterrupted()||( (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)) && !wt.isInterrupted() ) 
//1.如果线程池处于STOP、TIDYING、TERMINATED中的状态之一 ,且当前线程没有被中断,中断当前线程
//2.如果第一点不满足,只可能!wt.isInterrupted() 为 true ,runStateAtLeast(ctl.get(), STOP) 为 false。那么当前线程池的状态只可能处于RUNNING或SHUTDOWN状态 如果当前线程(可以理解为第一个任务线程)中断了,此时再重新检查一下线程池状态,如果线程池还处于STOP、TIDYING、TERMINATED中的状态之一,那么中断work中的线程(任务线程)
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
            //空方法实现
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                //运行任务
                    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;
                //记录完成任务的个数
                w.completedTasks++;
                //解锁
                w.unlock();
            }
        }
        //设置任务执行状态,为非正常结束
        completedAbruptly = false;
    } finally {
    //回收work
        processWorkerExit(w, completedAbruptly);
    }
}

彻底搞懂SHUTDOWN与STOP的区别(getTask)

如果从阻塞队列中获取到的任务都为 null 那么最终会进行processWorkerExit()操作(works.remove(work)),回收当前调用 getTask()的 work 对象

  1. 无论如何,只要是线程池状态为 STOP、TIDYING、TERMINATED状态中之一,此时获取不到任务会回收 work
  2. 线程池状态为 STOP、TIDYING、TERMINATED、SHUTDOWN 状态中之一,并且队列中为空,此时获取不到任务也会回收 work
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
        //CAS操作work数量减一
            decrementWorkerCount();
            return null;
        }

从上面这段代码也体现出了在阻塞队列还不为空的时候SHUTDOWN并不会立即切断线程池中的正在运行的任务的运行,STOP管你任务是不是正在执行,都会(让你获取不到任务)立即切断任务的执行了

  1. 默认情况:如果work队列为空,回收work,立即切断任务的执行
  2. 线程池中线程数大于最大线程数,且work队列不为空,也会进行回收work的操作,立即切断任务的执行
  3. 线程池中线程数小于最大线程数,但是 timedOut =true ,且work队列不为空,说明此任务(work)无效,清除此无效的 work,立即切断任务的执行
if ((wc > maximumPoolSize || (timed && timedOut))
    && (wc > 1 || workQueue.isEmpty())) { return null; }

getTask源代码

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?
//死循环
    for (;;) {
        int c = ctl.get();
        //获取线程池状态
        int rs = runStateOf(c);
        //条件转换:(rs >= SHUTDOWN && rs >= STOP) || (rs >= SHUTDOWN && workQueue.isEmpty())
        //1.如果线程池状态为 STOP、TIDYING、TERMINATED状态中之一,那么work数量减一,直接return null回收work
        //2.如果第一点不成立,要想进入if语句中,那么只可能是 第一点中的 rs >= STOP 为 false ,那么此时如果线程池状态为 STOP、TIDYING、TERMINATED、SHUTDOWN 状态中之一,并且阻塞队列中为空,此时也会work数量减一,直接return null 回收work
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
        //CAS操作work数量减一
            decrementWorkerCount();
            return null;
        }
//获取线程池中线程数量
        int wc = workerCountOf(c);
        //allowCoreThreadTimeOut默认为false
        //默认情况:如果线程池中的线程数量>核心线程数 那么 timed = true
        //其他情况:如果设置了allowCoreThreadTimeOut属性为true,那么timed = true
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//1.默认情况:work队列为空,回收work
//2.线程池中线程数大于最大线程数,且阻塞队列不为空,也会进行回收work的操作
//3.线程池中线程数小于最大线程数,但是 timedOut =true ,且阻塞队列不为空,说明此 work 无效,清除此无效的 work
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            //CAS对work数量减一
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            Runnable r = timed ?
            //拿出的任务带设置过期时间
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                //普通的拿出任务
                workQueue.take();
            if (r != null)
                return r;
                //拿出的work是无效的,设置 timedOut = true
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

如何回收work对象?(processWorkerExit)

我们所说的线程的个数其实就是work的数量,在移除work数量之后会进行关闭线程池的操作

private void processWorkerExit(Worker w, boolean completedAbruptly) {
        //如果任务为非正常结束进行CAS,work数量减一
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();
        final ReentrantLock mainLock = this.mainLock;
        //加锁防止并发
        mainLock.lock();
        try {
        //记录总的任务完成数量
            completedTaskCount += w.completedTasks;
            //移除work对象,
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
//尝试结束线程池
        tryTerminate();

        int c = ctl.get();
        //线程池状态为SHUTDOWN、RUNNING
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

尝试结束线程池(tryTerminate)体现

以下情况不会结束线程池

  1. 线程池是运行状态
  2. 线程池是TIDYING、TERMINATED状态说明线程池已经在结束了,无需重复结束
  3. 线程池状态为SHUTDOWN、并且阻塞队列不为空,不会去结束线程池,间接反应SHUTDOWN并不会立即切断线程池中的运行中任务
if (isRunning(c) ||
    runStateAtLeast(c, TIDYING) ||
    (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
    return;

tryTerminate源代码

final void tryTerminate() {
//死循环
        for (;;) {
            int c = ctl.get();
            //1.线程池是运行状态,无需关闭线程池
            //2.线程池是TIDYING、TERMINATED状态说明线程池已经在结束了,无需重复结束
            //3.线程池状态为SHUTDOWN、并且阻塞队列不为空,需等待任务运行完成,也不会去结束线程池
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
                //走到这说明线程池处于要关闭的状态,work数量不为 0,要进行回收work
            if (workerCountOf(c) != 0) { // Eligible to terminate
            //中断正在运行的work(任务线程),ONLY_ONE为true中断全部work,ONLY_ONE为false中断一个work
                interruptIdleWorkers(ONLY_ONE);
                return;
            }
//走到这说明work已经回收完毕,开始关闭线程池
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
            //更新线程池的状态为关闭前就绪状态
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                    //关闭线程池
                        terminated();
                    } finally {
                    //更新线程池状态为结束状态
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

到此线程池启动over------------------

线程池的关闭(shutdown)

线程池的关闭分为俩步:

  1. 改变线程池状态
  2. 中断works中的所有work(任务线程)执行
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    //检查权限,暂且不研究
        checkShutdownAccess();
        //改变线程池的状态
        advanceRunState(SHUTDOWN);
        //中断work(任务线程)执行
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    //关闭线程池
    tryTerminate();
}

线程池关闭步骤一(advanceRunState)

  • 调用shutdown()方法,改变线程池状态为 SHUTDOWN,无返回值,不会中断正在执行的work(任务线程)
  • 调用shutdownnow()方法,改变线程池状态为 STOP,有返回值,会返回为执行完的任务(runnable列表),会中断所有work(任务线程)

至于shutdownnow()与shutdown()的区别从源码角度上的体现,参考上文中STOP与SHUTDOWN的区别

advanceRunState(SHUTDOWN);
private void advanceRunState(int targetState) {
        for (;;) {
            int c = ctl.get();
            //如果此时线程池状态为SHUTDOWN、STOP、TIDYING、TERMINATED,表明线程池已经在关闭中,无需改变状态。
            //否则修改线程池状态
            if (runStateAtLeast(c, targetState) ||
                ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
                break;
        }
    }

线程池关闭步骤二(interruptIdleWorkers)

shutDown中断的是那些没有中断的work(任务线程),因为执行runWork代码的时候会进行w.lock()操作,详情查看runWork()方法,除非正在运行的任务释放lock,不然这里是中断不了work的

interruptIdleWorkers(false)
private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                //如果任务线程没有中断,且能获取到work的锁!!!!!
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                //如果onlyOne == true表明只中断一个work
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

shuDownNow如何中断work的?

中断全部work,无论你work是否正在执行任务,都给中断

知其然而知其所以然~线程池深入源码分析-手把手debug源码系列_thread_07

返回并未终止的runnable列表

知其然而知其所以然~线程池深入源码分析-手把手debug源码系列_多线程_08

到此线程池关闭OVER--------------完结🎉哦耶