编程之美!从线程池状态管理来看二进制操作之美

二进制操作在框架设计中被频繁使用,使用二进制在不同场景有提升计算速度较少内存占用等多种优点;

下面,我们依据线程池的状态管理,来看下怎么通过操作二进制对状态进行管理,过程中会发现编程之美~

线程池状态

首先,为了文章的完整性,我们还是先了解一下线程池的状态,总结如下如:

线程池状态分为5种RUNNINGSHUTDOWNSTOPTIDYINGTERMINATED

状态代表的含义

  • 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位存储, 上述的几种状态: 底层存储二进制为:

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 00110001 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值即可!

最后

上述,我们介绍了 线程池的状态 管理部分,主要通过不同位置的二进制来进行标识不同的状态,工作学习还会发现更多巧妙美妙的设计,等待着作为程序员我们去发现;

优秀的个人博客,低调大师

微信关注我们

原文链接:https://my.oschina.net/jiangxinJava/blog/4830404

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
Mario,低调大师唯一一个Java游戏作品

Mario,低调大师唯一一个Java游戏作品

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS

Oracle Database,又名Oracle RDBMS,或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是目前世界上流行的关系数据库管理系统,系统可移植性好、使用方便、功能强,适用于各类大、中、小、微机环境。它是一种高效率、可靠性好的、适应高吞吐量的数据库方案。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Sublime Text 一个代码编辑器

Sublime Text 一个代码编辑器

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。