Java线程--线程池ctl

线程池ctl

线程池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分析

为了简化分析现象,我举例一个字节8位(实际上,我们运行的机器现在都是32位或64位的),来分析: 

负数表示法

首先简要了解一下计算机中,负数的表示方法:补码表示法(原码的反码+1),

还有我们经常所说的正数的最高位是0,负数的最高位是1。(这种说法也对,但是描述不够全面)

二进制值(1字节) 十进制值
1000 0000红色的1代表负数蓝色的是补码(补码=反码+1) -128
1000 0001蓝色部分代表多大的值?:将补码还原为原码 -127想化成负数?:先减去1按位取反
1000 0010还原方法:补码-1再取反 -126
1000 0011 -125
... ...
1111 1110 -2
1111 1111 -1

首先我们看到,从-1到-128,其二进制的最高位都是1(表中标为红色)。

然后会发现,10000000 并没有拿来表示 -0;而10000001也不是拿来直观地表示-1。事实上,-1 用 11111111 来表示

怎么理解这个问题呢?先得问一句是-1大还是-128大

当 然是 -1 大。-1是最大的负整数。比如一个字节的数值中:1111 1111表示-1,那么,(11111111 - 1) 是什么呢?和现实中的计算结果完全一致。1111 1111 - 1 = 1111 1110,而1111 1110就是-2(也就是 -1减去1,是 -2)。这样一直减下去,当减到只剩最高位用于表示符号的1以外,其它低位全为0时,就是最小的负值了,在一字节中,最小的负值是1000 0000,也就是-128。请参考博客:java负数的二进制编码


移位操作符

以8字节举例,当前数字1左移2位,1<<2,1左移两位之后的值为4(左移几位,其意思就是1*2的几次方),计算机内的表示是

二进制 00000001 表示 十进制数值1

左移2位后(低位补零)

二进制00000100 表示 十进制数值4

所以说:1左移2位,1<<2,得到的数值是4(意思等同于1*(2的2次方))。移位操作请参考博客:移位操作符位运算


ctl原子变量包装环境

上源码:我以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位)

// 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


private static int ctlOf(int rs, int wc) { return rs | wc; }

假如:当前线程池是运行状态 rs = -1 并且有效线程是3个,那么ctlOf(rs,wc)方法的逻辑是什么意思呢?rs | wc 的翻译如下:

11100000 (运行状态)

或             (0|1为1,1|1为1,0|0为0,也就是说,或的位运算,有1就为真)

00000011(线程个数)

的值为:11100011 表示ctl当前的值:有3个线程,线程池处于运行状态


private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 

由以上的ctlOf()举例分析,知晓了,初始化的ctl值为11100000,高3位表示线程池目前处于运行状态,低5位表示线程0个


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位表示当前的线程池状态


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

得值为:00000011 会发现,workerCountOf()方法的目的就是低5位原是什么样,现运算后还是什么样,剔除掉了高3位的影响,我们通过workerCountOf()方法拿到了纯的当前状态的值000xxxxx xxxxx刚好是:当初规定好的低5位表示当前的线程个数


线程池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)

Atomically sets the value to the given updated value if the current value == the expected value.

Parameters:

expect - the expected value

update - the new value

Returns:

true if successful. False return indicates that the actual value was not equal to the expected value.

猜你喜欢

转载自blog.csdn.net/mmlz00/article/details/83895211