Java8 ThreadPoolExecutor 源码详解

Java8 ThreadPoolExecutor 源码详解

文章目录

      • Java8 ThreadPoolExecutor 源码详解
        • 1 简介
          • 1.1 什么是线程池
          • 1.2 使用简介
          • 1.3 核心概念简介
            • 1.3.1 四种类型workQueue简介
            • 1.3.2 四种饱和策略
          • 1.4 提交任务流程
          • 1.5 约定
        • 2 原理介绍
          • 2.1 线程池状态
            • runStateOf 方法
            • workerCountOf 方法
          • 2.2 内部Worker类介绍
          • 2.3 源码分析
            • 2.3.1 execute 方法分析
            • 2.3.2 addWorker 方法分析
            • 2.3.3 线程是如何启动并且保持存活的?
            • 2.3.4 processWorkerExit 方法分析
            • 2.3.5 tryTerminate 方法
        • 3 小结

1 简介

1.1 什么是线程池

​ 线程池思想:在系统中开辟一块区域,存放一定数量的线程,当接收到要处理的任务时,从池中取一个空闲的线程进行处理,执行完任务归还线程,线程池避免重复创建大量不必要线程,避免浪费cpu和系统资源

1.2 使用简介

​ ThreadPoolExecutor 主要通过 execute 和 submit方法提交要执行的任务到线程池,线程池会创建/取出空闲线程执行要处理的任务, execute 方法接收一个Runnable类型的对象参数且无返回值,submit 接收一个Callable类型的对象参数并且返回Future 类型对象返回值,Future类的get方法会阻塞等待线程执行完成并接收Callable执行完毕后的返回结果,使用简例如下

  public static void main(String args[]) {

    // 新建一个线程池
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        5,10,30, TimeUnit.SECONDS,
        new LinkedBlockingDeque<>()
    );

    // 提交任务(Runnable)
    // 输出hello world
    executor.execute(() -> {
      System.out.println("hello world");
    });

    // 关闭线程池
    executor.shutdown();
    
  }
  public static void main(String args[]) throws ExecutionException, InterruptedException {

    // 新建一个线程池
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        5,10,30, TimeUnit.SECONDS,
        new LinkedBlockingDeque<>()
    );

    // 提交任务(Callable)
    Future<String> future =  executor.submit(() ->  "hello world" );
    
    // 输出hello world
    System.out.println(future.get());

    // 关闭线程池
    executor.shutdown();

  }
1.3 核心概念简介

​ 接下来看一下ThreadPoolExecutor的一个构造方法并解析相关参数概念,结合1.4线程池处理任务流程会比较容易理解

    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;
    }

corePoolSize 最大核心线程数,提交任务到线程池时,如果线程池中线程数小于corePoolSize,那么线程池会新建一个线程来处理任务,而不管池中其他线程是否空闲

maximumPoolSize 最大线程数,提交任务到线程池时,如果线程池中线程数大于corePoolSize,而且任务队列已满,并且线程池中线程数小于maximumPoolSize,那么线程池会新建一个线程来处理任务

keepAliveTime 存活时间,非核心线程存活时间

unit 时间单位(秒,分钟,小时等等)

workQueue 任务队列,当提交任务时线程池中线程数等于corePoolSize,那么线程池会把任务放到一个阻塞队列中,空闲线程回到队列获取任务执行

threadFactory 线程工厂,默认即可

handler 饱和策略

线程池会有 <= corePoolSize 条线程一直存活,当线程池本身新建好的线程数小于corePoolSize 时,那么这些线程会一直存活在线程池,当线程池线程数大于corePoolSize , 线程池会选择空闲状态的线程,在空闲时间超过keepAliveTime 后移除这部分线程, 当线程池中线程数等于corePoolSize 时后将不再移除(但例外情况是线程池设置了allowCoreThreadTimeOut为true,那么不管线程池中线程数是否小于corePoolSize ,线程空闲时间超过keepAliveTime时间后都会消亡,这个值一般都会是false)

1.3.1 四种类型workQueue简介

​ workQueue是一个阻塞队列,当线程池中线程数超过corePoolSize 时,任务会被存储到阻塞队列中,等待空闲线程获取并且执行,workQueue目前主要有四种

  1. LinkedBlockingQueue 基于链表的无界阻塞队列,基于FIFO原则排序元素,吞吐量高于ArrayBlockingQueue
  2. ArrayBlockingQueue 基于数组的有界组设队列,基于FIFO原则排序元素
  3. SynchronousQueue 这是一个不存储阻塞元素的阻塞队列,为什么这样说呢,当一个任务提交到这个队列时,必须等待这个任务被线程取走才能继续入队,所以这个队列最多只能有一个任务,吞吐量高于LinkedBlockingQueue
  4. PriorityBlockingQueue 具有优先级的队列
1.3.2 四种饱和策略

​ 当线程池中线程数已经达到最大线程数,并且队列已经满的情况下,如果外界继续提交任务到线程池,那么线程池就会采用某种饱和策略去处理这个任务,四种饱和策略如下

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

Java8 ThreadPoolExecutor 源码详解_第1张图片

1.5 约定

​ 下文提到的workerCount指线程池线程数,runState指线程池状态

2 原理介绍

2.1 线程池状态

​ ThreadPoolExecutor 通过一个AtomicInteger 类型的变量ctl来控制线程池的状态和记录线程数量,我们知道Integer类型为32位,ctl的前3位用于表示线程池的状态,后29位用于表示线程池的线程数

    // 前3位表示状态,后29位表示线程池中线程数
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	// Integer.SIZE - 3 == 29 
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 1 左移29位减1,二进制为 00011111 11111111 11111111 11111111 
	// 表示线程池可创建的最大线程数为 2^29 - 1
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
	// 二进制为 11100000 00000000 00000000 00000000
    private static final int RUNNING    = -1 << COUNT_BITS;
	// 二进制为 00000000 00000000 00000000 00000000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
	// 二进制为 00100000 00000000 00000000 00000000
    private static final int STOP       =  1 << COUNT_BITS;
	// 二进制为 01000000 00000000 00000000 00000000
    private static final int TIDYING    =  2 << COUNT_BITS;
	// 二进制为 01100000 00000000 00000000 00000000
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    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; }
  1. RUNNING 可以接收新任务,并且会处理队列中存在的任务
  2. SHUTDOWN 不可以接收新任务,但仍会处理队列中存在的任务或者正在处理的任务
  3. STOP 不可以接收新任务,也不会处理队列中存在的任务,并且会中断正在被线程处理的任务
  4. TIDYING 所有任务已经被处理完或者被中止,workerCount为0,也就是线程池中线程数为0,经过这个状态之后会调用terminated()钩子方法
  5. TERMINATED terminated() 已经被执行完成
runStateOf 方法

​ runStateOf 方法用于计算当前线程池的运行状态

​ ~ 是按位取反的意思 , & 是按位与的意思

​ ~CAPACITY 二进制表示为 11100000 00000000 00000000 00000000

​ 假如参数c的二进制表示为 11100000 00000000 00000000 00000011

​ 那 c & ~CAPACITY 结果 为 11100000 00000000 00000000 00000000,线程池为RUNNING状态

​ ~CAPACITY 低29位都为0,高3位都为1,那么c & ~CAPACITY 后,c参数高3位保持原样,低29位都 为0,从而算出线程池当前状态

workerCountOf 方法

​ workerCountOf 方法用于计算当前线程池存在的线程数

​ CAPACITY 二进制表示 00011111 11111111 11111111 11111111

​ 假如参数c的二进制表示为 11100000 00000000 00000000 00000011

​ 那 c & CAPACITY 结果为 00000000 00000000 00000000 00000011,当前线程数为3

​ CAPACITY高三位为0,低29位为1,那么c & CAPACITY 后,c参数保持低29位不变,高3位都变成0,从而算出线程池当前线程数

2.2 内部Worker类介绍

​ ThreadPoolExecutor 类中存在一个Worker内部类,在下文中这个Worker字眼会频繁出现,那么这个Worker类究竟是什么,并且扮演什么角色?

其实线程池在接收到提交的任务,并且判定要去创建一个线程来处理任务的时候,ThreadPoolExecutor 首先会去新建一个Worker类型对象,这个对象维护着新建的线程对象和第一次接收的任务对象,在往后的文章描述会有更加详细的介绍,这里先大致有个概念, 在ThreadPoolExecutor 中有一个HashSet类型的workers变量,这个workers的size()就代表线程池中线程数的大小

/** * Set containing all worker threads in pool. Accessed only when * holding mainLock. */
private final HashSet workers = new HashSet();

Worker 继承了AQS类并且实现Runnable接口,所以Worker是一个可执行的任务并且又可以控制中断,作为锁控制的类,AQS 和 锁的原理内容推荐大家看这篇文章 https://blog.csdn.net/javazejian/article/details/75043422

    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;

        /** Thread this worker is running in.  Null if factory fails. */
        // 工作线程
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        // 第一个接收的任务,可能为空
        Runnable firstTask;
        /** Per-thread task counter */
        // 完成的任务个数
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        /**
           * 创建并初始化第一个任务,使用线程工厂来创建线程
           * 初始化有3步
           *1、设置AQS的同步状态为-1,表示该对象需要被唤醒
           *2、初始化第一个任务
           *3、调用ThreadFactory来使自身创建一个线程,并赋值给worker的成员变量thread
           */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** Delegates main run loop to outer runWorker  */
        // 把当前对象传递到runWorker方法执行
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // 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;
        }

        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) {
                }
            }
        }
    }
2.3 源码分析
2.3.1 execute 方法分析
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        // 获取ctl的值
        int c = ctl.get();
        // workerCountOf(c) 计算出线程池线程数,如果当前线程数小于corePoolSize
        // 那么调用addWorker去执行创建新线程的逻辑
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            // 如果创建新线程失败,重新获取ctl的值
            c = ctl.get();
        }
        // 如果线程池正处于RUNNING状态,那么把任务添加进入队列
        if (isRunning(c) && workQueue.offer(command)) {
            // 重新确认线程池状态,如果不是RUNNING状态,那么把任务从队列中移除
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果线程池处于RUNNING状态,但是线程池中线程数为0,那么新建一个线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 如果假如队列失败,调用addWorker方法去创建新线程
        else if (!addWorker(command, false))
            // 调用饱和策略处理线程池饱和情况
            reject(command);
    }

这个方法其实类似是一个模板化的方法,其中做了以下几个事情,其实对应1.4节中介绍的提交任务流程

  1. 判断当前线程池线程数,如果线程数小于corePoolSize, 那么调用方法去创建新线程
  2. 如果step1中线程池线程数大于corePoolSize,那么调用方法将任务加入到队列中
  3. 如果step2中任务加入队列失败,其实就是队列满的情况,那么调用方法去创建非核心线程
  4. 如果step3中创建非核心线程失败,那么调用reject方法,使用设置的饱和策略去处理这种情况
2.3.2 addWorker 方法分析
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            // 计算线程池状态
            int rs = runStateOf(c);

            // 如果是SHUTDOWN并且workQueue队列任务为空,或者>=STOP状态
            // 此时线程池处于关闭状态,不应该再接收任务
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                // 计算当前线程池的线程数
                int wc = workerCountOf(c);
                // 如果线程数大于CAPACITY(允许创建的最大线程数)
                // 或者要创建的是核心线程,但线程池核心线程数已经达到corePoolSize
                // 此时应该拒绝创建,返回false
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 通过CAS技术,对ctl进行加一操作,即workerCount+1,成功后跳出外层循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //如果当前runState不等于刚开始获取的runState,则跳出内层循环,继续外层循环
                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 对象,在Worker方法中,新建了一个线程对象赋值给Worker的thread变量
            // 并且把firstTask赋值给Worker的firstTask变量
            w = new Worker(firstTask);
            // 取出新建的线程对象
            final Thread t = w.thread;
            if (t != null) {
                // 加锁
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());
					// 如果rs是RUNNING状态,或者rs是SHUTDOWN状态,并且firstTask为空
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        // 也就是线程还未调用start()方法启动,但是已经是alive状态
                        // 或者线程池是SHUTDOWN状态,并且firstTask为空,应该抛出异常
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        // 把worker对象加入到workers集合中
                        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方法进行处理
                addWorkerFailed(w);
        }
        return workerStarted;
    }

2.3.3 线程是如何启动并且保持存活的?

​ 我们来看看Worker的构造方法,前面说了Worker 继承了AQS类并且实现Runnable接口,所以Worker是一个可执行的任务,Worker的构造方法除了把AQS的state设置成-1,赋值firstTask给Worker类的firstTask,还通过线程池的ThreadFactory的newThread方法创建了一个线程对象,接收的参数为this,即Worker对象本身,所以2.3.2代码中线程调用t.start()方法启动后,其实运行的是Worker类的run()方法

        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

​ 接下来看以下Worker类的run方法做了什么,这个方法只是简单的调用runWorker方法,并且把Worker对象本身作为参数传递

public void run() {    runWorker(this);}

​ 那么接下来我们重点看一下runWorker的实现

    final void runWorker(Worker w) {
        // 获取新建的线程对象
        Thread wt = Thread.currentThread();
        // 获取传递进来的任务
        Runnable task = w.firstTask;
        w.firstTask = null;
        // 把AQS状态设置成0,表示允许中断
        w.unlock(); // allow interrupts
        // 是否突然完成
        boolean completedAbruptly = true;
        try {
            // 如果task不为空或者getTask()不为空
            // getTask() 方法其实是去阻塞队列获取任务,获取不到就一直阻塞
            // 如果是第一次执行while循环并且w.firstTask不为空,这个task任务将被当前线程执行
            // 如果w.firstTask为空,那么就到阻塞队列等待任务,获取到就执行,没有就等待
            // 执行完毕后重复去阻塞队列获取任务执行,这就是线程为什么能够一直存活在线程池的原因了
            while (task != null || (task = getTask()) != null) {
                // 加锁
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                // 如果线程池大于等于STOP状态,或者线程状态被设置成中断状态
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    // 中断线程
                    wt.interrupt();
                try {
                    // 调用beforeExecute钩子方法
                    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钩子方法
                        afterExecute(task, thrown);
                    }
                } finally {
                    // 把task置空
                    task = null;
                    // 当前worker的完成任务数加一
                    w.completedTasks++;
                    // 释放锁
                    w.unlock();
                }
            }
            // 走到这一步证明while循环已经执行完毕,并且不是被突然中断的
            completedAbruptly = false;
        } finally {
            // 如果while循环退出,那么线程也随之消亡了,调用processWorkerExit进行后续处理
            processWorkerExit(w, completedAbruptly);
        }
    }

​ 这个方法其实做了以下几件事情

  1. 获取传递进来的任务,或者调用getTask()方法到队列拉取任务,处理完一个任务后while循环继续到阻塞队列去获取任务
  2. 获取到任务后,锁住worker对象
  3. 如果线程池被突然中止或者当前线程被设置成interrupt状态,那么中断当前线程
  4. 调用任务的run方法执行任务,执行完成后worker的completedTasks加一
  5. 任务处理完成后继续到队列拉取任务执行
  6. 如果因为超时或者其他原因while循环中止,那么线程随之结束生命周期,调用processWorkerExit方法进行一些处理

​ 接下来看一下getTask()方法是如何从阻塞队列获取任务的

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            // 获取线程池当前状态赋值为rs
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            // 如果线程池状态等于SHUTDOWN 并且队列为空
            // 如果线程池状态大于等于STOP
            // 那么当前线程池workerCount减一,返回空
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            
            // 获取当前线程池的workerCount
            int wc = workerCountOf(c);

            // Are workers subject to culling?
            // 判断当前线程池核心线程是否允许超时,或者当前线程数是否已经超过核心数
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            // 线程池线程数大于maximumPoolSize,并且timed为true,并且已经timeout
            // (timeout 在往下的代码会赋值) ,并且wc >1 ,并且workQueue已经为空,即任务已经处理完毕
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                // 如果CAS成功将workerCount减一,那么返回空
                if (compareAndDecrementWorkerCount(c))
                    return null;
                // 继续for循环,最终也是会走到这里取做workerCount减一操作
                continue;
            }

            try {
                // 如果time 为true,即核心线程允许超时,或者当前线程池线程数已经超过核心数
                // 那么调用阻塞队列的poll(keepAliveTime, TimeUnit.NANOSECONDS)方法
                // 那么线程池中如果一条线程处于空闲状态超过keepAliveTime秒后
                // 即超过keepAliveTime秒后还没有从队列中拉取到任务,即为null
                // 这个方法返回null后,runWorker的while循环退出,线程消亡
                // 如果time为false,那么会一直等待获取任务,
                // 一般会有<=corePoolSize条线程一直阻塞等待获取任务
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                // 把timedOut设置为true,下一次循环会直接返回null
                timedOut = true;
            } catch (InterruptedException retry) {
                // 把timedOut设置为false
                timedOut = false;
            }
        }
    }

​ getTask方法主要是到阻塞队列拉取任务

  1. 如果线程池状态为SHUTDOWN并且队列任务为空,或者线程池状态>SHUTDOWN,证明线程池被关闭,workerCount减一,直接返回空
  2. 线程池线程数大于maximumPoolSize,并且timed为true,并且已经timeout,那么workerCount减一,直接返回空
  3. 如果不是step1 和 step2的情况,那么一直阻塞等待队列的任务
2.3.4 processWorkerExit 方法分析

​ 通过对runWorker方法的分析,我们知道,在一个线程正常消亡或者或者意外消亡的时候会调用processWorkerExit 方法

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        // completedAbruptly 线程突然退出,workerCount减一
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();
		// 获取锁,增加线程池全局完成任务数
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            // 从workers集合把当前worker移除
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
		// 尝试终结,待会再看这个问题
        tryTerminate();

        int c = ctl.get();
        // 如果是SHUTDOWN状态并且队列不空,或者还在RUNNING状态
        if (runStateLessThan(c, STOP)) {
            // 如果不是突然退出,并且allowCoreThreadTimeOut为true
            // 即空闲状态下核心线程也会消亡
            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);
        }
    }

​ 这个方法主要做了一下三件事情

  1. 如果线程是突然中断,那么将workerCount减一,因为突然中断没来得及调整workerCount
  2. tryTerminate 尝试terminate线程池,即关闭线程池
  3. 如果是SHUTDOWN状态并且队列不空,或者还在RUNNING状态,说明还有任务需要处理,但是allowCoreThreadTimeOut为true,即核心线程经过一定时间也会消亡,那么保证线程池至少要有一条线程还在运行处理任务
2.3.5 tryTerminate 方法

​ 在说这个方法之前我想先说线程池中另外两个方法,shutdown 和 shutdownNow,优雅关闭线程池和立即关闭线程池,shutdown 会把线程池状态设置成SHUTDOWN 状态,但是线程池继续处理在队列中的任务或者正在处理的任务,shutdownNow会马上关闭线程池,并且不会继续处理所有任务

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 将线程池状态设置成SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 中断空闲线程
            interruptIdleWorkers();
            // 调用onShutdown钩子方法
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 尝试终结
        tryTerminate();
    }

    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 void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 遍历所有的worker对象,马上interrupt所有线程
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
    }

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

    private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 遍历worker对象,只中止空闲线程
            for (Worker w : workers) {
                Thread t = w.thread;
                // 怎么判定是不是空闲线程呢,调用worker对象的tryLock进行加锁
                // 加锁成功说明是空闲线程,执行中的线程是获取不了锁的,
                // 查看runWorker方法,线程每次while循环拉取到任务执行的时都会锁住worker对象
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        // 调用线程的interrupt方法进行中止
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                // 这个onlyOne应该传递过来的为false
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

​ 线程被interrupt后,再回来看getTask方法,队列workQueue拉取任务的时候会抛出InterruptedException,此时把timeout设置成false,进入下一轮拉取task的循环中

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                // 把timedOut设置为true,下一次循环会直接返回null
                timedOut = true;
            } catch (InterruptedException retry) {
                // 把timedOut设置为false
                timedOut = false;
            }
        }

​ 那在下一轮拉取task的循环中如果判定线程池状态时SHUTDOWN并且workerQueue不为空,则继续循环拉取任务,如果是STOP状态,那么返回null线程消亡,如果是SHUTDOWN并且workerQueue为空,那么那么返回null线程消亡,由于之前说了,shutdownNow会中断所有线程并且设置线程池状态STOP,那么如果是调用shutdownNow方法的话,线程池所有在队列等待任务的线程都马上返回,所有线程在runWorker的循环结束,所有线程马上消亡,SHUTDOWN并且workerQueue为空也同理

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                // workerCount减一
                decrementWorkerCount();
                return null;
            }

​ 这里有个问题,线程池如何优雅的关闭线程呢,其实就跟tryTerminate有关了,你有没有考虑过,调用shutdown的时候只是把线程调用shutdown方法当前时刻的空闲线程中断了,但是剩下的线程呢,等到workerQueue空的时候自行结束吗?其实并不是这样的,shutdown方法执行的时候会调用一次tryTerminate方法,tryTerminate又会调用interruptIdleWorkers(ONLY_ONE)方法获取一条空闲的线程中断它,当getTask返回后,runWorker方法会在最后调用processWorkerExit 方法,而processWorkerExit里也会调用一次tryTerminate去中断空闲线程,如此循环往复,达到优雅关闭的效果,流程如下

Java8 ThreadPoolExecutor 源码详解_第2张图片

​ 最后我们来看看tryTerminate的源码

    final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            // 如果是RUNNING 状态或者TIDYING状态
            // 或者SHUTDOWN状态但workerQueue不为空,那么直接返回
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            // 中断一个空闲线程
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            // 进入到这里证明线程池状态大于SHUTDOWN
            // 或者是SHUTDOWN状态但workerQueue为空
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 把状态设置为TIDYING
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                        // 调用terminated钩子方法
                        terminated();
                    } finally {
                        // 最后状态设置为TERMINATED
                        ctl.set(ctlOf(TERMINATED, 0));
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

​ tryTerminate方法执行完后线程池真正关闭,消亡

3 小结

​ 探索线程池源码的过程着实不容易,前前后后查阅资料,加上理解花了很多时间,同时也是第一篇个人的博客,探索的路上还有很多未知,以后继续努力,加油

你可能感兴趣的:(Java,java)