欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Java 线程池中的位操作 ThreadPoolExecutor

最编程 2024-06-16 09:20:17
...
【直播预告】上云 vs 下云:降本增笑?割韭菜?

前言

翻阅 Java 线程池的源码,可以看到用到了大量的位运算操作,本文来分析下这些位运算是如何计算的,以及最后算出的结果是什么。

正文

阅读之前,必须熟悉一下内容

  • & 与运算
  • | 或运算
  • ~ 取反
  • << 左移
  • 负数的二进制表示方式(先取对应 正数 的二进制,然后对每一位 取反,最后加 1)
  • java 中 位运算的优先级(取反 优先级大于 与运算

ThreadPoolExecutor 源码

ThreadPoolExecutor 用一个 int 类型来表示当前线程池的 运行状态线程有效数量

但是不同平台的 int 类型的范围不一样,我们先假定用 32 位的二进制表示 int 类型:(以下内容都是基于这个条件)

  • 高 3 位表示 线程池运行状态

  • 低 29 位表示 线程有效数量

    // 高 3 位 表示线程池状态,低 29 位表示线程个数
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // 线程个数的表示 位数(上面也提到了不同平台 int 类型范围不一样)  
    // 不管 int 是多少位, 反正高三位 就是表示线程状态,剩余的位数表示线程数量
    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;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // 获取线程池状态
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    
    // 获取线程池有效线程数量
    private static int workerCountOf(int c)  { return c & CAPACITY; }

    // 获取上面提到的 32 位 int 类型的数值
    private static int ctlOf(int rs, int wc) { return rs | wc; }

位运算详解

线程池的几个属性

  • COUNT_BITS = Integer.SIZE - 3;

    COUNT_BITS 表示用多少二进制位表示 线程数量, 这里假定 Integer.SIZE = 32 (注意: 这里不是 Integer.MAX_VALUE),所以最终结果 COUNT_BITS= 29

  • CAPACITY = (1 << COUNT_BITS) - 1

    CAPACITY 表示当前线程池中的最大线程数量,这个位运算可以拆解成 2 步:

    1. CAPACITY = 1 << 29 得到 1 000...0000(29 个 0)
    2. CAPACITY = CAPACITY-1 得到 1111...111 (29 个 1)
  • RUNNING = -1 << COUNT_BITS

    RUNNING 表示线程池处于 运行状态,COUNT_BITS 上面说到是 29,因此这个位运算就表示 -1 左移 29 位。

    -1 如果用 2 进制表示

    • 获取 -1 的正数,也就是 1 的二进制: 0000000....00000 1 (前面 31 位 0)

    • 对上一步进行取反, 1111111111...1111 0 (前面 31 位 1)

    • 对上一步 +1 操作, 111111111....1111 (32 位 1)

    因此 - 1 左移 29 位, 就得到了 111 0000..00000 ( 29个 0) 。 高三位 111 表示 RUNNING 状态

  • SHUTDOWN = 0 << COUNT_BITS

    如此类推, 高三位为 000

  • STOP = 1 << COUNT_BITS

    高三位为 001

  • TIDYING = 2 << COUNT_BITS

    高三位为 010

  • TERMINATED = 3 << COUNT_BITS

    高三位为 011

线程池的几个方法

参数 c 就是一开始提到了 32 位整数

1. runStateOf(int c)
	private static int runStateOf(int c)     
	 {
	     return c & ~CAPACITY;
	  }
  • ~ CAPACITY

    CAPACITY 从上面可以得知为 1111...111 (29 bit),取反后就是 111 0000000(29个 0)

  • c & 上面的结果

    就可以获取到 高三位,而后 29 位 全部为 0(1 & 0 或者 0 & 0都为 0)

2. workerCountOf(int c)
	private static int workerCountOf(int c)  { 
		return c & CAPACITY; 
	}

CAPACITY 为 000 111111(29个1)

因此 c & CAPACITY, 就可以获取变量 c 的低 29 位的值,高三位 与运算结果 为 0

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

rs 为 线程状态, wc 表示 线程数量, 或运算的结果就是,就相当于把 rs 的前三位,和 wc 的后 29 位,像字符串一样拼接 到了一起。

总结

上面提到的那些位运算操作是研究 ThreadPoolExecutor源码的基础,如果为了省事,也完全不用研究这么透彻, 记住文中开头的源码注释内容即可。

比如 CAPACITY 表示的最到线程数量就是 29 bit 1。

推荐阅读