总是出现的ctl
在研究线程池的源码的时候, 会经常见到这个常量
在execute方法中
在addWork方法中
可见这个常量是非常重要的, 那么这个常量是干啥的呢?
ctl的用处
我们先观察一下ctl的注释
简单说来, 这个用Integer的原子类AtomicInteger(原子整型)包装的字段ctl 是是用来表示状态, 而且这个字段里面表示的是两个变量, 第一是表示有效线程数 workerCount, (从第四位开始到最后) 表示线程池状态 runStatus.
ctl的包装和拆包
上文所说, ctl变量里面是有两个变量, 那么就一定会有如下操作: 把两个变量封装到ctl中(包装);从ctl中获取两个变量(拆包) 在介绍ctl的包装和拆包之前, 先介绍下面两个常量
其中Integer.SIZE 的值是32, 那么就是说COUNT_BITS的值为29, 为什么要减去3呢? 这个等下揭晓, 而CAPACITY的值为 前三位是0, 后29位为1 的二进制值.
这里解答为什么要减去3的问题, 因为3位二进制是用来表示线程池的5的状态, 这里使用 -1 ~ 3 自左移29位,去表示状态, 而不是直接用 -1 ~ 3 的原值去表示是有好处的.
拆包函数
runStateOf : 把ctl的值传进去, 首先把 CAPACITY做取反操作
(也就是反码, 变成 1110000....(29个0) ),
然后用ctl的值去做 & 操作取到runState的值. (& 操作 两个二进制数都是1才会取1否则都取0
(1&1=1, 1&0=0, 0&0=0),
由于此时CAPACITY的头三位是111, 假设此时线程池状态为010,也就是stop, 111 & 101也就是直接获取到010).
workerCountOf : 跟runStateOf 同理, 注意此时用的是CAPACITY的原码直接 & 操作,
假设此时ctl的值为 0100 0000....010(也就是有两个有效线程数),
跟 0001111....(29个1) 按& 操作, 就会直接获取 0000.....010, 就是当初表示工作线程的数字.
包装函数
包装方法,由于 runState的前三位才是有效数字(后面29位必定为0), 而worksCount的后29位才是有效数字(前三位必定为0), 所以进行或(|)运算就会得到一个32位有效数字的整型.
假设 : runState = 010 00000....0000
worksCount = 000..... 011,
| 操作就得到 010000....011.
ctl用于判断运行的状态
以下是ctl判断运行状态的方法
观察源码 可以看到这些方法的用法
可以看到,c是当前ctl的值, s是线程的状态,上面的代码是判断当前ctl是否大于STOP这个状态值
可以看到, c小于s的条件是当前线程池状态是RUNNING或者SHUTDOWN, 由于是高位对比,后29位是不是0没有影响.
修改worksCount的值
可以在addWorker中看到compareAndIncrementWorkerCount方法的调用
底层是基于原子整型的调用
总结
ctl是用来记录当前线程池的状态和运行的线程数, 具有原子性, 相当于线程池最重要的状态.