ThreadPoolExecutor源码历险-位运算技巧
我体会到的ThreadPoolExcutor的源码阅读可分为几部分
1.ThreadPoolExcutor构造函数(上一篇简书里面已经写过了)
2.位运算技巧
3.不可变内部类Worker
4.主流程常用方法
5.拒绝策略静态内部类
6.工作队列类型
构造函数就不多说了,请参考jdk源码或者移步上一篇
重要属性及位运算技巧
要说ThreadPoolExcutor里面的位运算技巧,首先要了解核心原子属性ctl
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
ctl是主要的池状态控制变量,是一个包装了两个概念字段的原子整数。
1.workerCount 有效线程数
2.runState 运行状态,运行、关闭等。
ctlOf的实现:
private static int ctlOf(int rs, int wc) { return rs | wc; }
也就是拿运行状态(rs),和有效线程数(wc)做位运算,位运算的意义是把这两个属性给整合到一块,比如:
STOP 001,00000000000000000000000000000
WorkCount:000,00000000000000000000000000001
ctl的最终值:001,00000000000000000000000000001
这里注意在ThreadPoolExcutor里面,状态有5种,并且只占最高位3位(左移了29位),剩下的低29位是给工作线程数量(wc)留的。
状态占用3位的原因是状态有5种,表示5种状态最少要用3位。
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
5种状态的解释:
RUNNING:接受新任务,并且可以处理任务队列中的任务
SHUTDOWN:不接受新任务,但是会处理队列中的剩余任务
STOP:不接受新任务,不处理排队任务以及中断进行中的任务
TIDYING:所有任务都已终止,workerCount为零,转换为状态TIDYING的线程将运行终止的hook方法terminated()
TERMINATED:terminated()方法执行完毕
这里有两个重要的方法runStateOf、workerCountOf要说一下,上面说过ThreadPoolExcutor里面状态会占用ctl的高三位,所以剩下的29位是用来表示线程数量的。
// 线程数最大限制
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 获取线程池运行状态
// ~CAPACITY的高三位是1,低29位是0。所以 c & ~CAPACITY 保留了前三位(状态)
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 获取线程有效线程数
// CAPACITY的高三位是0,低29位是1。所以 c & CAPACITY 保留了低29位(线程数)
private static int workerCountOf(int c) { return c & CAPACITY; }
由于ThreadPool源码篇幅比较长,所以我打算放到几篇里面来写。要不然一篇时间比较长,读起来大家也会失去耐心。其实ThreadPoolExecutor源码读起来比较容易懂,只要认真去看。大家熟悉了位运算技巧,后面代码逻辑都不是事。
后期我也会把我读源码中遇到的迷惑的问题抛出来,大家可以一起看一下。有知道的可以评论回答我