前言
不知道什么时候开始,我写文章变得小心翼翼的了,也许是怕自己知识不够,也许是怕深度不够。其实我清晰的知道是怕没人点赞。后面我直接不写文章了。这样就不怕了。
这篇主要为了实践前面的说的运算符。如果看得有点不明白,先看前面这篇文,谢谢。
线程池中源码中关于状态值
下面是线程池中源码中关于状态值,看着一大堆运算符都晕了,源码完全看不下去。我是哪一种有一个地方看不懂,就卡在这里,直到把它搞懂。
1 | private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); |
这一大堆运算符,只为服务代码中,AtomicInteger ctl
这个对象,线程池中,用这个值通过运算偏移的方式,保存了两个值。 核心目的是为了减少锁的竞争。我们先了解一下AtomicInteger
AtomicInteger
用原子方式更新的 int 值,底层实现用的是Unsafe
的 compareAndSwapInt
(乐观锁), 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。 如果成功,则返回 true。返回 False 指示实际值与预期值不相等。
问题来了,其实它这里只是存一个Integer值而已。如何实现存二个呢?作者 Doug Lea ,采用了运算符计算的方式,来保存两个值。Integer为32位,
源码中是采用高三位
保存线程状态(RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED这五个状态),三位最多可以存储7个状态
。注意:但包括0,加上负数,最多就是7个状态
。因为你要保障你的数都在高位,在低位的数值会被覆盖。低29位
保存当前运行的线程数量(workerCountOf),这个最大值为536870911
。所以线程数受限于这个值。 我想应该也没有人设置这么大吧!嘿嘿
图片直观感受一下
对这几行代码,进行剥丝抽茧。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//高3位用来存储状态,低29位存 当前运行的线程数量
private static final int COUNT_BITS = Integer.SIZE - 3;
//这个值的大小,看自己需求吧,这个写29是给线程数留住了最大空间了.(读写锁里面这个是16, 感兴趣可以去看看ReentrantReadWriteLock.)
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//这个值很关键,就是它用来分割一个值的。CAPACITY
不是随便写的,它的二进制, 都是29位的1, 1111111111111111,如果COUNT_BITS
是16,代入(1 << COUNT_BITS) - 1)中,
就是低位补16个0,减一个1,结果就是16个1,如果是2,就是2个1。
线程状态值
// runState is stored in the high-order bits
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;
重点我们看看这个表格,主要前面3位。
状态 | 二进制(32位) | 前3位 | 十进制 |
---|---|---|---|
RUNNING | 11100000000000000000000000000000 | 111 | -536870912 |
SHUTDOWN | 00000000000000000000000000000000 | 000 | 0 |
STOP | 00100000000000000000000000000000 | 100 | 536870912 |
TIDYING | 01000000000000000000000000000000 | 010 | 1073741824 |
TERMINATED | 01100000000000000000000000000000 | 011 | 1610612736 |
计算线程池状态
回顾一下前面的内容,我想应该忘得差不多了,能查到的资料,我也从来不记,我的头脑是让我思考用的。
&与运算 两个操作数中位都为1,结果才为1,否则结果为0。
~即0变成1,1变成0.(简单来说是加一,然后取反)比如 3 ,加一,是4,取反为-4.更直观的详细计算方式请看我上一章。
Thanks♪(・ω・)ノ
用RUNNING的状态来计算
1 |
|
运行的线程数量计算
我们假定目前线程运行的数量是1
,线程运行状态为RUNNING
。
1 | private static int workerCountOf(int c) { |
因为CAPACITY高位为000
,所以无论怎么算(&)都是取的低29位
。
更新线程池运行状态
或运算符用符号“|” ,两个位只要有一个为1,那么结果就是1,否则就为0。
private static int ctlOf(int rs, int wc) {
//比如目前状态为RUNNING,线程数量为1。那么值就是
//1110 0000 0000 0000 0000 0000 0000 0001
//0000 0000 0000 0000 0000 0000 0000 0000
//1110 0000 0000 0000 0000 0000 0000 0001 //结果
return rs | wc;
}
总结
下一篇来个,手写一个线程池吧!
本人知识有限,如有描述错误之处,愿虎正。
你看这个像不像你欠我的赞。
谢谢大家。
你的赞就像冬日暖阳,温暖心窝。