位操作与使用场景示例(ThreadPoolExecutor)

在学习ThreadPoolExecutor之前,需要先了解下此类源码依赖的基础知识。

一。位操作基础知识

二进制如何表示整数

Java的正数与负数的二进制表示:

Integer.toBinaryString(5)

//整数0开始,负数1开始,负数等于正数的反码再加一,左边是高位,右边是低位 正数5:0000 0000 0000 0000 0000 0000 0000 0101 反码 :1111 1111 1111 1111 1111 1111 1111 1010 加一 :1111 1111 1111 1111 1111 1111 1111 1011 负数5:1111 1111 1111 1111 1111 1111 1111 1011

负数表示相关知识:

  • 原码:正数原码,前面0变1.

  • 反码:最前面1不变,后面的取反。

  • 补码:最前面1不变,后面的取反,末位加1.

JAVA使用补码表示,补码的好处

``` 0000 0000 0000 0000 0000 0000 0000 0101

+ 1111 1111 1111 1111 1111 1111 1111 1011

0000 0000 0000 0000 0000 0000 0000 0000

```

常见的二进制运算

``` //~求反:1变0,0变1 正数5:0000 0000 0000 0000 0000 0000 0000 0101 反码 :1111 1111 1111 1111 1111 1111 1111 1010

//&求与,相乘,两个都为1才为1,其他情况均为0 5表示成:0000 0000 0000 0000 0000 0000 0000 0101 6表示成:0000 0000 0000 0000 0000 0000 0000 0110 进行& :0000 0000 0000 0000 0000 0000 0000 0100 得到:4

//|求或,或,都为0才为0其它都为1 5表示成: 0000 0000 0000 0000 0000 0000 0000 0101 6表示成: 0000 0000 0000 0000 0000 0000 0000 0110 进行| : 0000 0000 0000 0000 0000 0000 0000 0111 得到:7

//^不同,不同为1,相同为0 5表示成:0000 0000 0000 0000 0000 0000 0000 0101 6表示成:0000 0000 0000 0000 0000 0000 0000 0110 进行 ^ : 0000 0000 0000 0000 0000 0000 0000 0011 得到:3

//<<乘2 左边移动,右面填充0 5表示成: 0000 0000 0000 0000 0000 0000 0000 0101 进行 <<1 操作:0000 0000 0000 0000 0000 0000 0000 1010

//>>除以2 右边移动,左边填充0 5表示成: 0000 0000 0000 0000 0000 0000 0000 0101 进行 >>>1 操作:0000 0000 0000 0000 0000 0000 0000 0010

```

二。位操作在ThreadPoolExecutor使用场景(一个整数拆成两个用)

整数有32位,ThreadPoolExecutor就是把32位拆成了两部分:高3位是一个整数,表示运行状态;低29位表示运行的线程数。

取哪部分的值:直接和其他位为0的求&就可以,可以看下下边加了注释的代码,结合位的知识和代码中的二进制串,应该很容易看懂,不再赘述过多,通过实例看起来更轻松。

``` //ctl的值倍拆分为了两部分 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNTBITS = Integer.SIZE - 3; private static final int COUNTMASK = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
//RUNNING -1状态放入高三位 11100000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
//SHUTDOWN 0状态放入高三位 00000000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
//STOP 1状态放入高三位 00100000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
//TIDYING 2状态放入高三位 01000000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
//TERMINATED 3状态放入高三位 01100000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;

// Packing and unpacking ctl
//根据int值的低29位解析出状态,~COUNT_MASK=11100000000000000000000000000000
private static int runStateOf(int c) {
    return c & ~COUNT_MASK;
}

//根据int值的高3位解析出计数,COUNT_MASK=00011111111111111111111111111111
private static int workerCountOf(int c) {
    return c & COUNT_MASK;
}

//反向解析,传入运行状态和计数,得到int值
private static int ctlOf(int rs, int wc) {
    return rs | wc;
}

```

在学习ThreadPoolExecutor之前,需要先了解下此类源码依赖的基础知识。

三。CAS的基础知识

CAS,多个线程并发设置新值,通过传入老值,只有一个线程可以设置成功。JAVA提供Atomic相关类,比如AtomicBoolean的基本语法如下:

AtomicBoolean dealing = new AtomicBoolean(); //当原值是false的时候,设置为true,返回值是是否成功 dealing.compareAndSet(false, true);

下边用10个 线程了并发来设置,只有一个线程能够返回true

AtomicBoolean dealing = new AtomicBoolean(); for (int i = 0; i < 10; i++) { new Thread() { @Override public void run() { System.out.println(dealing.compareAndSet(false, true)); } }.start(); }

输出:

false true false false false false false false false false

四。位操作结合CAS在ThreadPoolExecutor中应用

CAS虽然是控制一个值的并发,但ThreadPoolExecutor把一个整数的不同位拆开来用,当作了两个变量。那么CAS就能达到控制两个变量并发安全的效果。在CAS看起来是一个变量,但却帮助程序两个变量一起实现了并发控制,不会有意外的多线程覆盖问题,两个变量类似做了事务,全部成功或全部失败。比如在advanceRunState方法中的用法:

``` //ctl的值倍拆分为了两部分 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

private void advanceRunState(int targetState) {
    // assert targetState == SHUTDOWN || targetState == STOP;
    for (; ; ) {
        int c = ctl.get();
        if (runStateAtLeast(c, targetState) ||
                //同时设置状态和计数,并发安全的事务保证
                ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值