关于线程池的ctl常量

329 阅读3分钟

总是出现的ctl

在研究线程池的源码的时候, 会经常见到这个常量

在execute方法中

Snipaste_2021-06-14_10-44-14.png

在addWork方法中

图片.png

可见这个常量是非常重要的, 那么这个常量是干啥的呢?

ctl的用处

我们先观察一下ctl的注释

图片.png

简单说来, 这个用Integer的原子类AtomicInteger(原子整型)包装的字段ctl 是是用来表示状态, 而且这个字段里面表示的是两个变量, 第一是表示有效线程数 workerCount, (从第四位开始到最后) 表示线程池状态 runStatus.

ctl的包装和拆包

上文所说, ctl变量里面是有两个变量, 那么就一定会有如下操作: 把两个变量封装到ctl中(包装);从ctl中获取两个变量(拆包) 在介绍ctl的包装和拆包之前, 先介绍下面两个常量

图片.png

其中Integer.SIZE 的值是32, 那么就是说COUNT_BITS的值为29, 为什么要减去3呢? 这个等下揭晓, 而CAPACITY的值为 前三位是0, 后29位为1 的二进制值.

图片.png

这里解答为什么要减去3的问题, 因为3位二进制是用来表示线程池的5的状态, 这里使用 -1 ~ 3 自左移29位,去表示状态, 而不是直接用 -1 ~ 3 的原值去表示是有好处的.

拆包函数

图片.png

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, 就是当初表示工作线程的数字.

包装函数

图片.png

图片.png

包装方法,由于 runState的前三位才是有效数字(后面29位必定为0), 而worksCount的后29位才是有效数字(前三位必定为0), 所以进行或(|)运算就会得到一个32位有效数字的整型.

假设 : runState =  010 00000....0000
      worksCount =  000..... 011, 
     | 操作就得到 010000....011.

ctl用于判断运行的状态

以下是ctl判断运行状态的方法

图片.png

观察源码 可以看到这些方法的用法

图片.png

可以看到,c是当前ctl的值, s是线程的状态,上面的代码是判断当前ctl是否大于STOP这个状态值

图片.png

可以看到, c小于s的条件是当前线程池状态是RUNNING或者SHUTDOWN, 由于是高位对比,后29位是不是0没有影响.

修改worksCount的值

图片.png

可以在addWorker中看到compareAndIncrementWorkerCount方法的调用

图片.png

底层是基于原子整型的调用

图片.png

总结

ctl是用来记录当前线程池的状态和运行的线程数, 具有原子性, 相当于线程池最重要的状态.