ThreadPoolExecutor 源码分析 - execute() 方法

前言

前面的两篇文章为分析 线程池源码 做了些铺垫

Java 线程池 ThreadPoolExecutor 中的位运算操作

线程池 ThreadPoolExecutor 源码分析基础 - 线程池工作原理

回顾下前面讲过的内容(需要事先掌握):

  • 线程池的几种状态 RUNNING 、SHUTDOWN 等
  • 线程池状态间的切换
  • 线程池的核心参数 corePoolSize 和 maximumPoolSize 等
  • 线程池的工作原理 核心线程 -> 阻塞队列 -> 非核心线程 -> 拒绝策略
  • 如何判断线程池的状态和线程数量:通过 AtomicInteger ctl 变量的高低位判断
  • Worker 对象的作用

掌握了以上知识点后, 再去分析具体源码就容易得多了。

正文

正文大纲

  • ThreadPoolExecutor 的 workers 属性
  • execute 方法
  • addWorker 方法
  • runWorker 方法
  • processWorkerExit 方法

ThreadPoolExecutor#workers 属性

线程池中有一个 workers 集合,里面记录了所有的工作线程,只有拿到 mainLock 锁的线程才能访问

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


// 该属性负责记录 workers 集合的长度, 也是需要拿到锁的线程才能访问 private int largestPoolSize; 

ThreadPoolExecutor#execute(Runnble command) 方法

上一篇文章 线程池 ThreadPoolExecutor 源码分析基础 - 线程池工作原理 大致分析了 execute 方法的执行流程, 这里再次提一次。

  • ctl.get() 前面提到过, 高 3 位 表示线程池状态,后面的位数表示线程个数
  • workerCountOf 为线程池的线程数量, 取得就是 ctl.get() 的后几位
  • addWorker 暂时可以理解为创建一个线程去执行具体的任务, 下面会详细讲
  • isRunning(c) 判断线程池是否处于运行的状态
  • reject 表示拒绝新添加的任务
public void execute(Runnable command) { // 参数校验 if (command == null) throw new NullPointerException(); // 从 c 的值可以判断出线程池的状态, 以及线程池中线程的数量 int c = ctl.get(); // 1. 如果线程池的线程数量 小于 核心线程数 if (workerCountOf(c) < corePoolSize) { // 添加一个核心线程 command 表示一个具体的任务, true 表示为核心线程 if (addWorker(command, true)) return; c = ctl.get(); } // 2. 如果线程池处于 RUNNING 状态(只有处于此状态,才能接受新的任务) // 并且线程池的数量大于核心线程数, 就把任务添加到阻塞队列 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); } // 3. 如果队列也满了,就创建一个非核心线程(core==false) else if (!addWorker(command, false)) // 如果创建失败,就执行拒绝策略 reject(command); } 

总结一下:

  1. 如果线程池的线程数量 小于 核心线程数, 添加一个核心线程 command
  2. 如果线程池处于 RUNNING 状态, 并且线程池的数量大于核心线程数, 就把任务添加到阻塞队列
  3. 如果队列也满了,就创建一个非核心线程
  4. 如果非核心线程创建失败,就执行拒绝策略

ThreadPoolExecutor#addWorker(Runnable, boolean)

该方法的作用暂时可以理解为:

创建并启动一个线程

该方法有两个参数:

  • Runnable firstTask 如果该参数不为空, 那么通过 addWorker 方法创建的线程会先去执行 firstTask

  • boolean core core 的值与一个 if 语句有关系

    如果 core == true ,就把线程池的数量与 corePoolSize 的值作比较, 反之, 与 maximumPoolSize 的值作比较。具体看源码分析

上面的 execute 中方法已经展示了 3 种 addWorker() 的调用方式

  • addWorker(firstTask, true)

  • addWorker(firstTask, false)

  • addWorker(null, false)

下面把 addWorker 方法分为两部分, 分割方法如下所示:

第一部分

该部分是个双层死循环

在死循环过程中,获取线程池状态, 判断线程池是否可以创建新的线程,或者是否可以执行阻塞队列中的任务。

该部分最终执行结果:

要么返回 false, 要么进入第二部分

而返回 false 有以下几种情况:

  • 线程池状态 > SHUTDOWN, 也就是不能接受新的任务,也不能处理队列中的任务
  • 线程池状态 == SHUTDOWN, 表示线程池可以处理队列中的任务,但是队列却为空
  • 或者线程池不能接受新的任务,但是 firstTask 却不为空
第二部分

如果顺利进入第二部分,就表示线程池可以创建新的线程

该部分的作用就是,通过 worker = new Worker() 创建 worker 对象,然后创建并开启线程,去执行 worker 对象的 run 方法。

private boolean addWorker(Runnable firstTask, boolean core) { // 一个标记, 类似 goto, 自行了解,结合下面的 break 和 continue 就能明白什么意思 retry: // 第一部分 for (;;) { int c = ctl.get(); // 获取线程池状态 int rs = runStateOf(c); // 返回 false 表示无法接收或处理任务 // 如果不想返回 false,也就是 if 语句判断不成立,需要满足以下两个条件之一: // 1. rs < SHUTDOWN, 也就是 rs == RUNNING(我们之前讲过 RUNNING 表示可以接受新的任务) // 2. rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty() // 2.1 rs == SHUTDOWN 表示不会接收新的任务,但是会执行阻塞队列中的任务 // 2.2 firstTask == nul 表示要执行阻塞队列中的任务 // 2.3 阻塞队列不能为空 if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()) ) return false; for (;;) { // 获取线程池中的线程数量 int wc = workerCountOf(c); // 如果线程数量大于最大容量(CAPACITY), 直接返回 false // 如果 core == true ,就判断线程数量是否大于核心线程数 // 如果 core == false, 就判断线程数量是否大于最大线程数 if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; // 通过 CAS 操作, 把线程池中的线程数量 + 1,然后跳出双层循环,进入第二部分 if (compareAndIncrementWorkerCount(c)) break retry; // CAS 操作失败,就核对线程池的状态 c = ctl.get(); if (runStateOf(c) != rs) // 继续从最外层循环开始执行 continue retry; } } // 第二部分 // 走到这里,表示线程池可以创建新的线程,或者可以执行队列中的任务 // 两个标记,见名知义 boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { // 下面两行代码上一篇文章介绍 Worker 对象时,详细说明过 // 包装需要执行任务(注意 firstTask 可能为 null) 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()); // 如果线程池状态为 RUNNING // 或者 // 如果线程池状态为 SHUTDOWN 且 firstTask == null, if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) throw new IllegalThreadStateException(); // 添加至 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 { // 如果线程启动失败, 就删除 workers 集合中刚添加的 Worker 对象 if (! workerStarted) addWorkerFailed(w); } return workerStarted; } 

Worker#run

run 方法中调用 runWorker方法

public void run() { runWorker(this); this 表示具体的 worker 对象 } 

ThreadPoolExecutor#runWorker

在 addWorker 方法源码的第二部分中,会先通过 new Worker(firstTask) 创建 worker 对象,此时:

  • 传入的参数 firstTask != null 时 , 表示线程启动后第一个执行的任务就是 firstTask
  • 而传入的参数 firstTask == null 时 ,上面也讲了 firstTask == null 仅仅表示要求线程池去执行阻塞队列中的任务。

然后拿到 worker 对象的 thread 去启动线程, 最后执行 runWorker 方法

简要概括:

runWorker 方法会先去执行 addWorker 时传入的 firstTask, 如果 firstTask == null, 就去执行阻塞队列中的任务

当两者都为空时, runWorker 方法执行结束。

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    // 注意这一步把 firstTask 重新赋值给了 task Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { // 1. task !=null, 就直接执行 task // 2. task == null, 表示调用 addWorker 方法时传入 的 firstTask == null // 所以就通过 getTask 方法去阻塞队列中获取任务 while (task != null || (task = getTask()) != null) { w.lock(); 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 { // 如果连阻塞队列中都没有任务,线程的任务就算是处理完了,最后做一些善后工作 processWorkerExit(w, completedAbruptly); } } 

Worker#processWorkerExit

private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // 统计线程池完成的任务个数 completedTaskCount += w.completedTasks; workers.remove(w); } finally { mainLock.unlock(); } // 尝试设置线程池状态为 TERMINATED, tryTerminate(); // 如果线程池的线程数量小于核心线程是, 则增加一个线程 int c = ctl.get(); 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); } } 

总结

分析 ThreadPoolExecutor 源码前, 需要了解以下内容:

  • 线程池的数量和状态是由 Integer.SIZE - 3 个 2 进制位表示的, 其中高 3 位 表示线程池状态,其余低位表示线程个数

  • 获取线程池数量和状态都是基于上面的二进制位来计算的

    例如:

	private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1; private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static int runStateOf(int c) { return c & ~CAPACITY; } private static int workerCountOf(int c) { return c & CAPACITY; } 
  • 线程池的几种状态, 以及状态之间如何切换的

  • 线程池 Worker 对象的作用

    	 Worker w = new Worker(firstTask);
    	 Thread t = w.thread;
    	 t.start();
    	 线程运行时,会去执行 Worker#run 方法
    	然后 run 方法会去调用 ThreadPoolExecutor#runWorker(); 方法
    	如果 firstTask != null , runWorker 就会去执行 firstTask 的 run 方法
    	如果 firstTask == null , runWorker 就会去执行阻塞队列中的任务
    

猜你喜欢

转载自www.cnblogs.com/vwvwvwgwg/p/12892351.html