线程池之计算机组成原理

90 阅读6分钟

本次不讲线程池的运行逻辑,只讲一下线程池中状态的切换和一些其他属性方法

1、状态属性

private static final int COUNT_BITS = Integer.SIZE - 3;
// 最大容量
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// 运行中;能接受新提交的任务,并且也能处理阻塞队列中的任务。
private static final int RUNNING    = -1 << COUNT_BITS;
// 关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程
private static final int STOP       =  1 << COUNT_BITS;
// 所有的任务都已经终止了,workerCount(有效线程数)为0
private static final int TIDYING    =  2 << COUNT_BITS;
// 在terminated()方法执行完成后进入该状态
private static final int TERMINATED =  3 << COUNT_BITS;

1.1、CAPACITY

首先 Integer.SIZE的值是32,即COUNT_BITS的值是29

private static final int COUNT_BITS = 29;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
// 上面的步骤是1左移29为再减一,结果如下
//           1: 0000 0000 0000 0000 0000 0000 0000 0001
// (1 << 29)  : 0010 0000 0000 0000 0000 0000 0000 0000
// (1 << 29)-1: 0001 1111 1111 1111 1111 1111 1111 1111

private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

1.2、RUNNING

先求-1的二进制,在左移29位,负数的二进制需要先求反码再求补码

    private static final int RUNNING    = -1 << COUNT_BITS;
    // 先求负一
    //        1原码 : 0000 0000 0000 0000 0000 0000 0000 0001
    //         反码 : 1111 1111 1111 1111 1111 1111 1111 1110
    //       -1补码 : 1111 1111 1111 1111 1111 1111 1111 1111
    //     -1 << 29 : 1110 0000 0000 0000 0000 0000 0000 0000

1.3、SHUTDOWN

先求0的二进制,在左移29位

    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //        0原码: 0000 0000 0000 0000 0000 0000 0000 0000
    //    0 << 29 : 0000 0000 0000 0000 0000 0000 0000 0000   

1.4、STOP

先求1的二进制,在左移29位

    private static final int STOP       =  1 << COUNT_BITS;
    //        1原码 : 0000 0000 0000 0000 0000 0000 0000 0001
    //      1 << 29 : 0010 0000 0000 0000 0000 0000 0000 0000

1.5、TIDYING

先求2的二进制,在左移29位

    //        2原码 : 0000 0000 0000 0000 0000 0000 0000 0010
    //    2 << 29 : 0100 0000 0000 0000 0000 0000 0000 0000
    private static final int TIDYING    =  2 << COUNT_BITS;

1.6、结束状态 TERMINATED

先求3的二进制,在左移29位

    private static final int TERMINATED =  3 << COUNT_BITS;   
    //      3原码 : 0000 0000 0000 0000 0000 0000 0000 0011
    //    3 << 29 : 0110 0000 0000 0000 0000 0000 0000 0000 
RUNNING    :1110 0000 0000 0000 0000 0000 0000 0000
SHUTDOWN   :0000 0000 0000 0000 0000 0000 0000 0000
STOP       :0010 0000 0000 0000 0000 0000 0000 0000
TIDYING    :0100 0000 0000 0000 0000 0000 0000 0000
TERMINATED :0110 0000 0000 0000 0000 0000 0000 0000

所有的状态只用到了最高3位

2、主要方法

2.1、获取运行状态-runStateOf

~:二进制按位取反

&:二进制计算只要有一个为0就是0

// 比如c=1110 0000 0000 0000 0000 0000 0000 0001;  c & ~CAPACITY
//         CAPACITY: 0001 1111 1111 1111 1111 1111 1111 1111
//       ~CAPACITY : 1110 0000 0000 0000 0000 0000 0000 0000
//                c: 1110 0000 0000 0000 0000 0000 0000 0001
//    c & ~CAPACITY: 1110 0000 0000 0000 0000 0000 0000 0000
private static int runStateOf(int c)     { return c & ~CAPACITY; }

这里的做法说白了就是让后面的29位全部失效,只看高三位

2.2、获取线程数量-workerCountOf

// 比如c=1110 0000 0000 0000 0000 0000 0000 0001;  c & CAPACITY
//               c: 1110 0000 0000 0000 0000 0000 0000 0001
//        CAPACITY: 0001 1111 1111 1111 1111 1111 1111 1111
//    c & CAPACITY: 0000 0000 0000 0000 0000 0000 0000 0001
private static int workerCountOf(int c)  { return c & CAPACITY; }

这里说白了就是抛弃掉高三位,剩下的29位作为线程池总数

注意:有的地方说线程池最多可以创建Integer.MAX_VALUE个线程,这里是错误的,最多创建0001 1111 1111 1111 1111 1111 1111 1111(即536 870 911)个线程。

详细见addWorker方法

// 线程总数数大于536 870 911
// 如果是创建核心线程,不能大于核心线程数;如果创建非核心线程数则不能大于非核心线程数
// 否则返回false
if (wc >= CAPACITY ||
    wc >= (core ? corePoolSize : maximumPoolSize))
    return false;

2.3、获取ctl-ctlOf

// rs:线程池状态,wc:线程池线程数量
// |:二进制计算只要有一个为1就是1
// 举例子 状态是RUNING 线程数为1
//     rs=RUNNING: 1110 0000 0000 0000 0000 0000 0000 0000
//         wc =1 : 0000 0000 0000 0000 0000 0000 0000 0001
//        rs | wc: 1110 0000 0000 0000 0000 0000 0000 0001
private static int ctlOf(int rs, int wc) { return rs | wc; }

这里就是把线程状态和线程数合并成一个新的二进制数据

3、结束

我第一次看到这里的时候,心里是这么想的:代码写的真好,下次别写了。

这代码挺消耗队友的