这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
在之前的文章中我们已经知道了线程池的底层特性,包括线程复用大部分都是使用worker对象来实现的,最后在线程池中我们还剩下一些控制相关的代码:
- ctl
CTL
在了解ctl前,我们需要先了解一下位算法。
位算法
我们知道:
- 计算机底层并不是原生实现的四则逻辑运算;相比起人类的四则运算,计算机的原生逻辑是二进制的位算法,而不是十进制基础下的加减乘除。
- 因此,在相同的操作次数以及规模下,位运算要比加减乘除更快一些。
java中的位算法,大概包括以下几个操作符:
- 与运算 & : 两位都是1,结果位为1
- 或运算 | :两位有一个为1,结果位就为1
- 异或运算^: 两位相同为0,否则为1
- 非运算(取反) ~ : 1变0,0变1
- 左移(<<)和右移(>>) : 数往左(右)移动若干位数,高位(低位)溢出丢弃,低位(高位)补0。
知道了这些,我们就能看懂线程池里ctl具体是如何工作的了。
ctl
根据注解以及之前的理解,我们现在知道:
- ctl是一个atmoicInteger类型的值,同时记录了线程池运行的状态,以及线程池中工作线程的数量。
我们很容易地能找到线程池状态相关的常量部分:
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;
这样一来我们就知道:
- 我们使用ctl的高3位来代表线程池的运行状态。
同时,我们使用剩下的29位(我们知道,29位代表着数不大于2^29-1)代表线程池中worker线程的多少,这个数我们如此记录:
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
两值存取
在ThreadPoolExecutor源码中,一般是使用副本copy的方式进行比较:
int c = ctl.get();
//check which condition threadPool in
....
因此源码中开放了对于这两个值的存取。
-
运行状态:
private static int runStateOf(int c) { return c & ~CAPACITY; }这里:
~CAPACITY 就等于:11100000000000000000000000000000(高三位为1,剩余为0)
那么c&~CAPACITY就意味着取c的高三位的数据。
-
工作线程数量
private static int workerCountOf(int c) { return c & CAPACITY; }
这里
& CAPACITY就意味着取ctl里低29位里的数据。
\