前言
前面的两篇文章为分析 线程池源码 做了些铺垫
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); }
总结一下:
- 如果线程池的线程数量 小于 核心线程数, 添加一个核心线程 command
- 如果线程池处于 RUNNING 状态, 并且线程池的数量大于核心线程数, 就把任务添加到阻塞队列
- 如果队列也满了,就创建一个非核心线程
- 如果非核心线程创建失败,就执行拒绝策略
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 就会去执行阻塞队列中的任务