【多线程】ThreadPoolExecutor类源码解析----续(二进制相关运算)

前言

在之前阅读 ThreadPoolExecutor 源码的时候,发现代码里用到了一些二进制相关的位运算之类的代码,看起来有些费劲了,所以现在大概总结了一些笔记,二进制这东西吧,不难,就跟数学一样,知道规律,计算公式,就贼简单,就是二进制转十进制这种自己算起来比较费劲

但现在又不是考试,所以我选择计算器!!!!

二进制

计算机采用二进制原因:

  • 首先,二进位计数制仅用两个数码。0和1,所以,任何具有二个不同稳定状态的元件都可用来表示数的某一位。而在实际上具有两种明显稳定状态的元件很多。例如,氖灯的;开关的; 电压的;纸带上的有孔无孔,电路中的有信号无信号, 磁性材料的南极北极等等,不胜枚举。 利用这些截然不同的状态来代表数字,是很容易实现的。不仅如此,更重要的是两种截然不同的状态不单有量上的差别,而且是有质上的不同。这样就能大大提高机器的抗干扰能力,提高可靠性。而要找出一个能表示多于二种状态而且简单可靠的器件,就困难得多了。
  • 其次,二进位计数制的四则运算规则十分简单。而且四则运算最后都可归结为加法运算和移位,这样,电子计算机中的运算器线路也变得十分简单了。不仅如此,线路简化了,速度也就可以提高。这也是十进位计数制所不能相比的 。
  • 第三,在电子计算机中采用二进制表示数可以节省设备。可 以从理论上证明,用三进位制最省设备,其次就是二进位制。但由于二进位制有包括三进位制在内的其他进位制所没有的优点,所以大多数电子计算机还是采用二进制。此外,由于二进制中只用二个符号 “ 0” 和“1”,因而可用布尔代数来分析和综合机器中的逻辑线路。 这为设计电子计算机线路提供了一个很有用的工具。
  • 第四,二进制的符号“1”和“0”恰好与逻辑运算中的“对”(true)与“错”(false)对应,便于计算机进行逻辑运算。
二进制运算

二进制加法有四种情况: 0+0=0,0+1=1,1+0=1,1+1=10(0 进位为1)

二进制乘法有四种情况: 0×0=0,1×0=0,0×1=0,1×1=1

二进制减法有四种情况:0-0=0,1-0=1,1-1=0,0-1=1

二进制除法有两种情况(除数只能为1):0÷1=0,1÷1=1

java中的位运算

Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。

位运算符作用在所有的位上,并且按位运算。假设a = 60,b = 13;它们的二进制格式表示将如下:

        A = 0011 1100
        B = 0000 1101
        -----------------
        A & B = 0000 1100
        A | B = 0011 1101
        A ^ B = 0011 0001
        ~A    = 1100 0011

下表列出了位运算符的基本运算,假设整数变量 A 的值为 60 和变量 B 的值为 13:

操作符描述例子
如果相对应位都是1,则结果为1,否则为0(A&B),得到12,即0000 1100
如果相对应位都是 0,则结果为 0,否则为 1(A 丨B)得到61,即 0011 1101
^如果相对应位值相同,则结果为0,否则为1(A ^ B)得到49,即 0011 0001
按位取反运算符翻转操作数的每一位,即0变成1,1变成0。(〜A)得到-61,即1100 0011
<<按位左移运算符。左操作数按位左移右操作数指定的位数。A << 2得到240,即 1111 0000
>>按位右移运算符。左操作数按位右移右操作数指定的位数。A >> 2得到15即 1111
>>>按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。A>>>2得到15即0000 1111

测试栗子:

    public class BitOperation {
        public static void main(String[] args) {
            int a = 60; /* 60 = 0011 1100 */
            int b = 13; /* 13 = 0000 1101 */
            int c = 0;
            c = a & b;       /* 12 = 0000 1100 */
            System.out.println("a & b = " + c );
    
            c = a | b;       /* 61 = 0011 1101 */
            System.out.println("a | b = " + c );
    
            c = a ^ b;       /* 49 = 0011 0001 */
            System.out.println("a ^ b = " + c );
    
            c = ~a;          /*-61 = 1100 0011 */
            System.out.println("~a = " + c );
    
            c = a << 2;     /* 240 = 1111 0000 */
            System.out.println("a << 2 = " + c );
    
            c = a >> 2;     /* 15 = 1111 */
            System.out.println("a >> 2  = " + c );
    
            c = a >>> 2;     /* 15 = 0000 1111 */
            System.out.println("a >>> 2 = " + c );
        }
    }

输出:

    a & b = 12
    a | b = 61
    a ^ b = 49
    ~a = -61
    a << 2 = 240
    a >> 2  = 15
    a >>> 2 = 15

ThreadPoolExecutor 源码分析

AtomicInteger ctl

ctl是主要的控制状态,是一个复合类型的变量,其中包括了两个概念。

  • workerCount:表示有效的线程数目
  • runState:线程池里线程的运行状态

我们来分析一下跟ctl有关的一些源代码吧,直接上代码

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    
    //用来表示线程池数量的位数,很明显是29,Integer.SIZE=32
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //线程池最大数量,2^29 - 1
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    
    // runState is stored in the high-order bits
    //我们可以看出有5种runState状态,证明至少需要3位来表示runState状态
    //所以高三位就是表示runState了
    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; }

CAPACITY: 线程最大数量

在这里我们讲一下这个线程池最大数量的计算吧,因为这里涉及到源码以及位移之类的操作,我感觉大多数人都还是不太会这个,因为我一开始看的时候也是不太会的。

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

从代码我们可以看出,是需要1往左移29位,然后再减去1,那个1往左移29位是怎么计算的呢?

    1 << COUNT_BITS
    
    1322进制是
    0000 0000 0000 0000 0000 0000 0000 0001
    
    左移29位的话就是
    0010 0000 0000 0000 0000 0000 0000 0000
    
    再进行减一的操作
    0001 1111 1111 1111 1111 1111 1111 1111
    
    也就是说线程池最大数目就是
    0001 1111 1111 1111 1111 1111 1111 1111

runState:线程池里线程的运行状态

正数的原码、反码、补码都是一样的,在计算机底层,是用补码来表示的

    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;
RUNNING 运行状态

可以接受新任务并且处理已经在阻塞队列的任务,高3位全部是1的话,就是RUNNING状态

    -1 << COUNT_BITS
    这里是-1往左移29位,稍微有点不一样,-1的话需要我们自己算出补码来
    
    -1的原码
    1000 0000 0000 0000 0000 0000 0000 0001
    
    -1的反码,负数的反码是将原码除符号位以外全部取反
    1111 1111 1111 1111 1111 1111 1111 1110
    
    -1的补码,负数的补码就是将反码+1
    1111 1111 1111 1111 1111 1111 1111 1111
    
    关键了,往左移29位,所以高3位全是1就是RUNNING状态
    1110 0000 0000 0000 0000 0000 0000 0000
SHUTDOWN 关闭状态

不接受新任务,处理已经在阻塞队列的任务,高3位全是0,就是SHUTDOWN状态

    0 << COUNT_BITS
    
    0的表示
    00000000 00000000 00000000 00000000
    
    往左移2900000000 00000000 00000000 00000000
STOP

不接受新任务,也不处理阻塞队列里的任务,并且会中断正在处理的任务,所以高3位是001,就是STOP状态

    1 << COUNT_BITS
    
    1的表示
    00000000 00000000 00000000 00000001
    
    往左移2900100000 00000000 00000000 00000000
TIDYING

所有任务都被中止,workerCount是0,线程状态转化为TIDYING并且调用terminated()钩子方法,所以高3位是010,就是TIDYING状态

    2 << COUNT_BITS
    
    2322进制
    00000000 00000000 00000000 00000010
    
    往左移2901000000 00000000 00000000 00000000
TERMINATED

terminated()钩子方法已经完成,所以高3位是110,就是TERMINATED状态

    3 << COUNT_BITS
    
    3322进制
    00000000 00000000 00000000 00000011
    
    往左移2911000000 00000000 00000000 00000000

相关方法介绍

runStateOf

实时获取runState的方法

    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    ~CAPACITY
    ~是按位取反的意思
    &是按位与的意思
    
    而CAPACITY是,高位30,低29位都是1,所以是
    000 11111 11111111 11111111 11111111
    
    取反的话就是
    111 00000 00000000 00000000 00000000
    
    传进来的c参数与取反的CAPACITY进行按位与操作
    1、低位290进行按位与,还是290
    2、高位31,即保持c参数的高3位
    即高位保持原样,低29位都是0,这也就获得了线程池的运行状态runState
workerCountOf

获取线程池的当前有效线程数目

    private static int workerCountOf(int c)  { return c & CAPACITY; }
    CAPACITY的322进制是
    000 11111 11111111 11111111 11111111
    
    用入参c跟CAPACITY进行按位与操作
    1、低29位都是1,所以保留参数c的低29位,也就是有效线程数
    2、高3位都是0,所以c的高3位也是0
    
    这样获取出来的便是workerCount的值
ctlOf

原子整型变量ctl的初始化方法

//结合这几句代码来看
    private static final int RUNNING    = -1 << COUNT_BITS;
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    RUNNING是
    1110 0000 0000 0000 0000 0000 0000 0000
    
    ctlOf是将rs和wc进行按位或的操作
    
    初始化的时候是将RUNNING和0进行按位或
    
    0322进制是
    0000 0000 0000 0000 0000 0000 0000 0000
    
    所以初始化的ctl是
    1110 0000 0000 0000 0000 0000 0000 0000
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值