您的位置:首页 > 其它

线程池源码-伟大而渺小的ctl

2020-04-01 12:59 190 查看

最近看到有些博客提到线程池需要掌握的问题清单,发现自己很多地方是是一知半解的状态,正好借此机会,带着问题去回顾了一波 Java 线程池的源码。

ctl 为何物?

线程池的运作过程对状态的检查非常严格,几乎是走两步一个检查,检查线程池的状态,有效线程的数目,而它们都是基于一个整型变量来实现的,它的「身子」也许很单薄,但是肩上的责任重大。

// 本质是 Integer 型变量,进行了原子性的封装
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

一饰两角

大家可能会想,一个变量怎么能够同时表示两个状态,我当时也非常疑惑,这里就要提到一个巧妙的设计——高低位表示法(名字是我瞎掰的)。

1.紧随在 ctl 变量后面被初始化的两个变量

private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

COUNT_BITS 的值为 29(整型 32 位);

CAPACITY = 1 << 29 表示将 1 往左移动了 29 位,换算之后就是 1 * 2 的 29 次方,高 3 位被空出来了,而低 29 位用来表示 CAPACITY 即线程池的最大线程容量

1
0000 0000 0000 0001
1 << 29 - 1
0001 1111 1111 1111

2.高 3 位用来表示状态,因为有 5 种状态,需要 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;

状态通过数字大小区分,最小的是 RUNNING 状态,最大的是 TERMINATED 状态,一个预示着开始,一个代表结束,逐渐递增,会经过很多中间状态,状态间的转换后面的文章会详细说明。

3.通过 ctl 怎么取得具体状态呢?我们说的高 3 位表示法又是怎么实现的呢?这就要依赖于之前计算好的 CAPACITY 变量。

private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// 0001 1111 1111 1111 二进制表示

来看看线程池获取状态的方法代码,参数 c 是 ctl 变量当前的值。

private static int runStateOf(int c)     { return c & ~CAPACITY; }

首先对 CAPACITY 进行「取反」操作。

0001 1111 1111 1111
~ 操作符取反,可以看到低 29 位都变成了 0
1110 0000 0000 0000

ctl 和取反后的 CAPACITY 进行 & 运算,熟悉 & 运算的都知道,1 & 1 = 1;1 & 0 = 0,运算之后,ctl 低 29 位都会被置为 0,通过这个方法来屏蔽掉低 29 位的数值干扰。

4.获取有效线程数量也同理,高 3 位的数值被屏蔽掉

private static int workerCountOf(int c)  { return c & CAPACITY; }

经过一顿分析,我们得知线程池就是通过以上骚操作来实现 ctl 一饰两角的「表演」。

所以在阅读线程池源码的过程中,不要惊讶为什么通过一个 ctl 即能够获取线程数量,又能得知线程池状态。另外还会频繁出现类似以下条件表达式,rs 表示的是当前线程状态。

rs >= SHUTDOWN
rs >= STOP

因为从 RUNNING 到 TERMINATED 状态间的取值是单调递增的,所以可以通过此方法来进行条件判定。

希望看完这篇文章,能够帮助你更流畅地阅读 Java 线程池源码,干就完事了!

  • 点赞
  • 收藏
  • 分享
  • 文章举报
春娇的志明 发布了15 篇原创文章 · 获赞 0 · 访问量 153 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: