目录
一、ctl介绍
二、线程池ctl源码
三、线程池ctl分析
1、private static int ctlOf(int rs, int wc) { return rs | wc; }
2、private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
3、private static int runStateOf(int c) { return c & ~CAPACITY; }
4、private static int workerCountOf(int c) { return c & CAPACITY; }
四、线程池ctl如何管理池状态和线程数
一、ctl介绍
ThreadPoolExecutor中有一个控制状态的属性叫ctl,它是一个AtomicInteger类型的变量,它包含两个概念:
- workerCount:表明当前有效的线程数
- runState:表明当前线程池的状态,是否处于Running,Shutdown,Stop,Tidying,Terminate五种状态。
为了把这两种状态放到一个int值(共32位)中保存,代码中限定了workerCount的值是2^29-1,因为还有五种状态需要表示,至少需要3位才能表示五种状态(3位二进制数最大能到6),所以会有29位来表示workerCount,而剩下的3位来表示当前线程池的状态。
从上面的代码中可以看出COUNT_BITS这个属性就是Integer.SIZE-3,也就是29。说明线程数所占位数为29位,而CAPACITY得到的就是1向左无符号移29位-1,得到的就是低28位全是1的536870911。而看到上图下面的五个状态,分别是-1,0,1,2,3向左无符号移29位。
位数计算
- 从上图可以看到workerCountOf这个函数传入ctl之后,是通过ctl & CAPACITY操作来获取当前运行线程总数的。也就是RunningState | WorkCount & CAPACITY,算出来的就是低28位的值。因为CAPACITY得到的就是高3位(29-31位)位0,低28位(0-28位)都是1,所以得到的就是ctl中低28位的值。
- 而runStateOf这个方法的话,算的就是RunningState | WorkCount & ~CAPACITY,高3位的值,因为~CAPACITY是CAPACITY的取反,所以得到的就是高3位(29-31位)为1,低28位(0-28位)为0,所以通过&运算后,所得到的值就是高3为的值。
- 从而理解了ctl中是高3位作为状态值,低28位作为线程的线程数量来进行存储的原因。
二、线程池ctl源码
打开ThreadPoolExecutor的源码(我裁剪掉了一部分),一开始就会发现:
public class ThreadPoolExecutor extends AbstractExecutorService {
/** 我以一个字节8位来简化解释线程池对运行状态和当前有效线程个数的原子管理方案
* 线程池当中,用一个ctl原子变量包装了高3位的运行状态和低5位的线程个数
*
* 运行状态: 线程池初始化后,就处于该状态:此时,线程池可以接受新任务并且处理任务
* 关闭状态: 调用shutdown()方法时,就处于该状态:此时,shutdown()方法之后不能再提交新任务,线程池会把shutdown()方法之前提交的任务按照线程池工作原理的步骤都处理完毕。(请参考我的博客:线程池工作原理)
* 停止状态: 调用shutdownNow()方法时,就处于该状态:此时,shutdownNow()方法之后不能再提交新任务,线程池不处理已经提交到任务队列中的任务,线程池尝试中断正在执行的工作线程
* 整理状态: 线程池内部自己使用的状态:当线程池queue任务队列为空,hashset<worker>为空时,就是该状态,该状态是由关闭状态/停止状态转变而来的。当处于整理状态时,线程池会调用terminated()钩子方法
* 终结状态: 当钩子方法terminated()执行完毕之后,线程池由整理状态转变为终结状态。钩子方法是线程池自动调用的。
*
* 在线程池终结状态之前,可以调用awaitTermination()阻塞方法,使当前主线程阻塞,直至线程状态转变为终结状态
*/
/**
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*
* The runState provides the main lifecycle control, taking on values:
*
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workers is zero,the thread transitioning to state TIDYING
* TERMINATED: terminated() has completed
*
* RUNNING -> SHUTDOWN On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP On invocation of shutdownNow()
* SHUTDOWN -> TIDYING when both queue and pool are empty,will run the terminated() method
* STOP -> TIDYING When pool is empty
* TIDYING -> TERMINATED when the terminated() hook method has completed
*
* Threads waiting in awaitTermination() will return when the state reaches TERMINATED.
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 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;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
.......
}
三、线程池ctl分析
负数的表示方法:补码表示法(原码的反码+1),
我以8字节举例(同理适应于32位或64位机器)
上源码:我以8字节举例(同理适应于32位或64位机器)
private static final int COUNT_BITS = Integer.SIZE - 3; //8-3=5 活跃线程支持5位来表示个数
private static final int CAPACITY = (1 << COUNT_BITS) - 1; //1左移5位-1(1*2的5次方-1),也就是31个线程(低5位) 也就是00000001左移5位是00100000,然后再减00000001得到最终结果00011111将低5位标识线程数的部分保留
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS; // -1即11111111 左移5位后为11100000 表示运行状态(高3位)
private static final int SHUTDOWN = 0 << COUNT_BITS; // 0即00000000 左移5位后为00000000 表示关闭状态(高3位)
private static final int STOP = 1 << COUNT_BITS; // 1即00000001 左移5位后为00100000 表示停止状态(高3位)
private static final int TIDYING = 2 << COUNT_BITS; // 2即00000010 左移5位后为01000000 表示关闭状态(高3位)
private static final int TERMINATED = 3 << COUNT_BITS; // 3即00000011 左移5位后为01100000 表示关闭状态(高3位)
不难发现,高3位很好的表示5种状态 000 SHUTDOWN,001 STOP,010 TIDYING,011 TERMINATED,111 RUNNING
1、private static int ctlOf(int rs, int wc) { return rs | wc; }
假如:当前线程池是运行状态 rs = -1 并且有效线程是3个(ws),那么ctlOf(rs,wc)方法的逻辑是什么意思呢?rs | wc 的翻译如下:
11100000 (运行状态)
或 (0|1为1,1|1为1,0|0为0,也就是说,或的位运算,有1就为真)
00000011(线程个数)
的值为:11100011 表示ctl当前的值:有3个线程,线程池处于运行状态
该方法用于初始化ctl
2、private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
由以上的ctlOf()举例分析,知晓了,初始化的ctl值为11100000,高3位表示线程池目前处于运行状态,低5位表示线程0个
3、private static int runStateOf(int c) { return c & ~CAPACITY; }
这里的c是ctl.get()得到的原子值(AtomicInteger原子类中的volatile变量),也可以理解为当前ctl的值,这个值的高3位表示线程池的运行状态,低5位表示当前的线程个数。
c & ~CAPACITY 翻译如下:
假如c的值当前就是上面的举例:11100011 表示有3个线程,线程池处于运行状态
~CAPACITY 的运算即为:00011111求反,得值为11100000
c & ~CAPACITY
11100011(高3位表示运行状态)
与 (0&1为0,1&1为1,0&0为0,也就是说,与的位运算,有0必为假)
11100000
得值为:11100000 会发现,runStateOf()方法的目的就是高3位原是什么样,现运算后还是什么样,剔除掉了低5位的影响,我们通过runStateOf()方法拿到了纯的当前状态的值xxx00000 xxx刚好是:当初规定好的高3位表示当前的线程池状态
4、private static int workerCountOf(int c) { return c & CAPACITY; }
有了以上 c & ~CAPACITY 翻译后,这里就较好理解,下面翻译下 c & CAPACITY
假如c的值当前就是上面的举例:11100011 表示有3个线程,线程池处于运行状态
c & CAPACITY
11100011(高3位表示运行状态)
与 (0&1为0,1&1为1,0&0为0,也就是说,与的位运算,有0必为假)
00011111
四、线程池ctl如何管理池状态和线程数
翻看ThreadPoolExecutor的源码会发现:
ctl的初始化:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }
ctl在线程池运行期间,有大量的方法都调用了:
ctl.compareAndSet(expect, update); //这个操作是原子操作,表示设置当前ctl的值。
可参考AtomicInteger原子类的compareAndSet()方法
public final boolean compareAndSet(int expect, int update)
相关文章:【线程池】Java的线程池
【线程池】Java线程池的核心参数
【线程池】Executors框架创建线程池
【线程池】ScheduledExecutorService接口和ScheduledThreadPoolExecutor定时任务线程池使用详解 【线程池】线程池的拒绝策略(饱和策略)