【源码粗读】 - [3]ThreadPool - CTL以及位算法

258 阅读2分钟

这是我参与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位里的数据。

\