【Java】编程之美!从线程池状态管理来看二进制操作之美
二进制操作
在框架设计中被频繁使用,使用二进制在不同场景有提升计算速度
、较少内存占用
等多种优点;
下面,我们依据线程池的状态管理,来看下怎么通过操作二进制对状态进行管理,过程中会发现编程之美~
线程池状态
首先,为了文章的完整性,我们还是先了解一下线程池的状态,总结如下如:
线程池状态分为5种:RUNNING
、SHUTDOWN
、STOP
、TIDYING
、TERMINATED
状态代表的含义
- RUNNING:(运行)接收新task,并且处理正在排队的task,不中断正在执行的任务
- SHUTDOWN:(关闭)不接受新的task,只处理正在排队的task,不中断正在执行的任务
- STOP:(停止)不接受新的task,也不处理正在排队的task,并且中断正在执行的任务
- TIDYING:(整理)所有的task都已经终止,上述提到的workCount当前活跃线程数为0,被中断的任务和正在排队的任务执行当前任务的terminated()钩子方法
- TERMINATED:(已终止)标识上述的TIDYING的过程结束,标识当前线程池成功完全停止的状态
状态转换
大致的流程就是:
RUNNING
--> SHUTDOWN
--> STOP
--> TIDYING
--> TERMINATED
上述流程是一个单方向的顺序,也就是说不会出现类似于STOP
--> SHUTDOWN
这种情况;
另外,并不是每一个状态多必须经过的;
什么时候进行线程池的状态转换呢?
- RUNNING -> SHUTDOWN:调用终止线程的方法
shutdown()
后 - RUNNING or SHUTDOWN -> STOP:调用
shutdownNow()
方法后,不管当前在RUNNING状态还是SHUTDOWN状态,都是直接转为STOP状态 - SHUTDOWN -> TIDYING:SHUTDOWN状态下当等待队列 和 正在执行的任务 都为空时,状态转为TIDYING
- STOP -> TIDYING:STOP状态下当正在执行的任务全部中断完毕后,状态转为TIDYING
- TIDYING -> TERMINATED:TIDYING状态下当所有的terminated()钩子方法全部执行完毕后,状态转为TERMINATED,线程池关闭完毕!
管理线程池状态
线程池中管理线程池状态 和 线程池当前活跃线程数,是通过一个AtomicInteger变量来管理这两个状态的
什么? 一个变量管理两个这么不相干的状态? 对的;
CTL变量何许人也
让我们来看一下线程池针对这部分的实现:
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; }
private static boolean isRunning(int c) { return c < SHUTDOWN;}
下面,我们来剖析一下上述的实现:
线程池包含5种状态如下:具体线程的状态代表的含义和状态的转换,下面会有讲解:
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;
我们知道在java中 int 类型占用4个字节32位存储, 上述的几种状态:
底层存储二进制为:
左移<<COUNT_BITS位
, COUNT_BITS = Integer.SIZE - 3
也就是 COUNT_BITS = 29
,改句子说明用32位的前3位存储线程池的状态
后29位存储线程池中当前线程的个数, << COUNT_BITS
后,变为下面的二进制:
我们可以看到,前三位存储的是 标识线程状态的二进制
对于初始化存储这些状态的变量 AtomicInteger ctl
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0))
初始化AtomicInteger变量ctl
,其中ctlOf(RUNNING, 0)
代码为:
private static int ctlOf(int rs, int wc) { return rs | wc; }
其中rs标识线程池当前状态,wc为work count标识当前工作线程的数量
上述传入的是ctlOf(RUNNING, 0)
,当前状态为RUNING也就是1110 0000 0000 0000 0000 0000 0000 0000
,wc为0,也就是当前工作线程数为0,其二进制为0000 0000 0000 0000 0000 0000 0000 0000
,做"|"
或操作,即
上述得到的结果1110 0000 0000 0000 0000 0000 0000 0000
就标识,当前线程池状态为RUNNING,线程池活跃线程个数为0!
如何管理?
通过上述创建的ctl变量获取 线程池当前状态 和 线程中活跃线程个数 这两个状态:
获取线程池当前状态,我们可以想一下该如何获取呢? 现在知道的是ctl的前3位是线程池的状态,那我们直接构造一个前三位为1,后29位为0的int即可,然后取余就可以了呗,下面看下源码的实现,就是如此:
使用方法runStateOf
private static int runStateOf(int c) { return c & ~CAPACITY; }
其中CAPACITY = (1 << COUNT_BITS) - 1
转化为二进制为:0001 1111 1111 1111 1111 1111 1111 1111
取反"~"后,二进制为:1110 0000 0000 0000 0000 0000 0000 0000
也就是将前3位全部变为1,后面全部变为0;
接下来,传入的ctl变量和~CAPACITY
做“&”操作,只会保留ctl变量的前3位变量,后29位变量全部为0;
例如:一个标识当前状态为STOP状态的线程池和当前活跃线程数为3的ctl变量为:0010 0000 0000 0000 0000 0000 0000 0011
和上述得到的1110 0000 0000 0000 0000 0000 0000 0000
做“&”操作后得到:0010 0000 0000 0000 0000 0000 0000 0000
和上述分析的STOP的状态的二进制相同! 即获得了当前线程的状态!
获取线程池当前状态,也很简单,我们知道ctl变量的32的后29位存储的是当前活跃线程数,直接构造一个前三位为0,后29位为1的int即可,然后取余就可以获取到了
使用方法workerCountOf
private static int workerCountOf(int c) { return c & CAPACITY; }
上述知道CAPACITY为:0001 1111 1111 1111 1111 1111 1111 1111
例如:一个标识当前状态为STOP状态的线程池和当前活跃线程数为3的ctl变量为:0010 0000 0000 0000 0000 0000 0000 0011
和 0001 1111 1111 1111 1111 1111 1111 1111
取与后:0000 0000 0000 0000 0000 0000 0000 0011
标识当前线程池中活跃线程数量为3!
一些方法
1、计算ctl的值
方法:
private static int ctlOf(int rs, int wc) { return rs | wc; }
其中,入参rs代表当前线程状态,wc代表当前活跃线程数,取“|”或即可
上述代码不出现问题的前提是:rs只使用的前3位,wc只使用了后29位!
2、判断当前线程池是否正在运行
方法:
private static boolean isRunning(int c) { return c <小于SHUTDOWN;}值即可!
上述我们知道,5中状态只有RUNNING小于0,SHUTDOWN状态等于0,其他的都是大于0的,所以我们直接把给定的ctl值小于SHUTDOWN值即可!
最后
上述,我们介绍了 线程池的状态 管理部分,主要通过不同位置的二进制来进行标识不同的状态,工作学习还会发现更多巧妙美妙的设计,等待着作为程序员我们去发现;
以上是 【Java】编程之美!从线程池状态管理来看二进制操作之美 的全部内容, 来源链接: utcz.com/a/92674.html