本篇内容介绍了“如何从线程池状态管理来看二进制操作”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
首先,为了文章的完整性,我们还是先了解一下线程池的状态,总结如下如:
线程池状态分为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变量来管理这两个状态的
什么? 一个变量管理两个这么不相干的状态? 对的;
让我们来看一下线程池针对这部分的实现:
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位存储, 上述的几种状态: 底层存储二进制为:
1111 1111 1111 1111 1111 1111 1111 1111(-1) 0000 0000 0000 0000 0000 0000 0000 0000(0) 0000 0000 0000 0000 0000 0000 0000 0001(1) 0000 0000 0000 0000 0000 0000 0000 0010(2) 0000 0000 0000 0000 0000 0000 0000 0011(3)
左移<<COUNT_BITS位
,COUNT_BITS = Integer.SIZE - 3
也就是 COUNT_BITS = 29
,改句子说明用32位的前3位存储线程池的状态 后29位存储线程池中当前线程的个数, << COUNT_BITS
后,变为下面的二进制:
1110 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 0000 0000 0000 0000 0000 0000 0000 0100 0000 0000 0000 0000 0000 0000 0000 0110 0000 0000 0000 0000 0000 0000 0000
我们可以看到,前三位存储的是 标识线程状态的二进制
对于初始化存储这些状态的变量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 | 0000 0000 0000 0000 0000 0000 0000 0000 = 1110 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值即可!
“如何从线程池状态管理来看二进制操作”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。