1、变量定义
ThreadPoolExecutor中有一个重要的变量ctl,定义如下:
看注释可以知道,这个变量有两个作用:
1、记录有效的线程数量,即 workerCount
2、记录当线程池的状态,即 runState
2、原理分析
这个变量是如何同时记录这两个值的呢?答案是:位运算。下面详细说明一下
首先说线程池的状态,根据代码可以知道,线程池的状态有5种:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED。如果用二进制表示的话,这5种状态至少需要3位来表示,而一个 int 类型的数占32位,所以,高3位表示线程状态(runState),剩下的29位表示线程数量(workerCount)。线程数量最大值为 00011111 11111111 11111111 11111111,即 2^29 - 1。
为什么要低29位来保存 workerCount,因为线程状态这个数据只关心能否表示5个值,并不关心具体数值,而 workerCount 是需要计算具体数据的,所以低29位可以直接拿来用,并不需要额外的运算(注:如果用高29位表示 workerCount,还需要进行右移操作才能取得真正的数值)。
3、相关方法
主要分析一下与 ctl 相关的方法和变量。
COUNT_BITS:等于29。即 workerCount 所占的位数。
CAPACITY:1左移29位减1=2^29-1,workerCount 的最大值。单词意思为容量,很形象。
下面是5种状态:-1、0、1、2、3分别左移29位,占据高3位。可以看到,源码中是用11100000,00000000,00100000,01000000,01100000来表示5种状态的。(注:应该是32位,为了省事没有全写出来,剩下的24位全部为0)
把两个数值存在同一个变量的原理已经了解了,那么当需要分别读取 runState 和 workerCount 时,又要怎么获取各自的数值呢?图3的代码给出了答案
主要通过 &(按位与)操作符来实现两个数据的拆分。若要获取高3位,&11100000 00000000 00000000 00000000,可以保留高3位,把低29位全部变为0,再与上边定义的常量进行比较即可。若要获取低29位,&00011111 11111111 11111111 11111111,可以把高3位变为0,低29位保留。
至于两个数据的组装,则是通过 |(按位或)运算符实现的,大家可以自己尝试分析一下。