关于java:编程之美从线程池状态管理来看二进制操作之美

43次阅读

共计 4327 个字符,预计需要花费 11 分钟才能阅读完成。

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

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

线程池状态

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

线程池状态分为 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 值即可!

最初

上述,咱们介绍了 线程池的状态 治理局部,次要通过不同地位的二进制来进行标识不同的状态,工作学习还会发现更多奇妙美好的设计,期待着作为程序员咱们去发现;

正文完
 0