【多线程】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 1100B = 0000 1101-----------------A & B = 0000 1100A | B = 0011 1101A ^ 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 = 12a | b = 61a ^ b = 49~a = -61a << 2 = 240a >> 2 = 15a >>> 2 = 15
ThreadPoolExecutor 源码分析
AtomicInteger ctl
ctl是主要的控制状态,是一个复合类型的变量,其中包括了两个概念。
- workerCount:表示有效的线程数目
- runState:线程池里线程的运行状态
我们来分析一下跟ctl有关的一些源代码吧,直接上代码
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//用来表示线程池数量的位数,很明显是29,Integer.SIZE=32private static final int COUNT_BITS = Integer.SIZE - 3;//线程池最大数量,2^29 - 1private 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 ctlprivate 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_BITS1的32位2进制是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的补码,负数的补码就是将反码+11111 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_BITS0的表示00000000 00000000 00000000 00000000往左移29位00000000 00000000 00000000 00000000
STOP
不接受新任务,也不处理阻塞队列里的任务,并且会中断正在处理的任务,所以高3位是001,就是STOP状态
1 << COUNT_BITS1的表示00000000 00000000 00000000 00000001往左移29位00100000 00000000 00000000 00000000
TIDYING
所有任务都被中止,workerCount是0,线程状态转化为TIDYING并且调用terminated()钩子方法,所以高3位是010,就是TIDYING状态
2 << COUNT_BITS2的32位2进制00000000 00000000 00000000 00000010往左移29位01000000 00000000 00000000 00000000
TERMINATED
terminated()钩子方法已经完成,所以高3位是110,就是TERMINATED状态
3 << COUNT_BITS3的32位2进制00000000 00000000 00000000 00000011往左移29位11000000 00000000 00000000 00000000
相关方法介绍
runStateOf
实时获取runState的方法
private static int runStateOf(int c) { return c & ~CAPACITY; }
~CAPACITY~是按位取反的意思&是按位与的意思而CAPACITY是,高位3个0,低29位都是1,所以是000 11111 11111111 11111111 11111111取反的话就是111 00000 00000000 00000000 00000000传进来的c参数与取反的CAPACITY进行按位与操作1、低位29个0进行按位与,还是29个02、高位3个1,即保持c参数的高3位即高位保持原样,低29位都是0,这也就获得了线程池的运行状态runState
workerCountOf
获取线程池的当前有效线程数目
private static int workerCountOf(int c) { return c & CAPACITY; }
CAPACITY的32位2进制是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 0000ctlOf是将rs和wc进行按位或的操作初始化的时候是将RUNNING和0进行按位或0的32位2进制是0000 0000 0000 0000 0000 0000 0000 0000所以初始化的ctl是1110 0000 0000 0000 0000 0000 0000 0000
【多线程】ThreadPoolExecutor类源码解析----续(二进制相关运算)相关推荐
- java.lang 源码剖析_java.lang.Void类源码解析
在一次源码查看ThreadGroup的时候,看到一段代码,为以下: /* * @throws NullPointerException if the parent argument is {@code ...
- Node 学习六、核心模块 events之 01 events 和 EventEmitter 类、发布订阅、EventEmitter 类源码解析和模拟实现
events 事件模块 events 与 EventEmitter node.js 是基于事件驱动的异步操作架构,内置 events 模块 events 模块提供了 EventEmitter 类 这个 ...
- Scroller类源码解析及其应用(一)
滑动是我们在自定义控件时候经常遇见的难题,让新手们倍感困惑,这篇文章主要介绍Scroller类的源码,告诉打击这个到底有什么用,怎么使用它来控制滑动.另外,我还会结合一个简单的例子,来看一下这个类的应 ...
- Java FileReader InputStreamReader类源码解析
FileReader 前面介绍FileInputStream的时候提到过,它是从文件读取字节,如果要从文件读取字符的话可以使用FileReader.FileReader是可以便利读取字符文件的类,构造 ...
- Java String类源码解析
String直接继承Object 含有一个char[] value,还有一个int hash默认值为0 new String()的构造产生的是一个值为""的字符数组 String( ...
- Java集合---Arrays类源码解析
一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型: ...
- Unsafe类源码解析
前言 Unsafe,顾名思义,一个不安全的类,那么jdk的开发者为什么要设计一个不安全的类呢?这个类为什么会不安全呢?现在就让我们来揭开Unsafe类的神秘面纱. 1.概述 作为java开发者的我们都 ...
- Java多线程——Thread Runnable源码解析
Java多线程的两种实现方法大家都应该知道了:继承Thread的子类实例化和实现Runnable接口用这个接口实现类去创建Thread实例. Java的线程在Linux平台上使用的是NPTL机制,JV ...
- Java代码覆盖率框架JaCoCo的core-instr core.internal.instr 包类源码解析
对类的植入锁定进行判断 几个可以对覆盖率跟踪的Java类定义进行instrument的API public byte[] instrument(final ClassReader reader) {f ...
最新文章
- docker-compose的安装与简单使用
- 大神程序员,夜夜coding到天明?Python之父昼伏夜出,PHP创始人24小时都在线
- C++ int与string的转化
- 关于application title一直是untitled的问题
- 如何在vs中创建r树索引代码_线段树详解与实现
- 极大似然估计和贝叶斯估计
- 野生前端的数据结构基础练习(6)——集合
- 每日算法系列【LeetCode 992】K个不同整数的子数组
- html引用ttf字体文件
- 怎样给div增加resize事件
- python写sql语句_Python 数据分析:让你像写 Sql 语句一样,使用 Pandas 做数据分析...
- 背诵华为hcia认证考试题库答案能过吗?华为认证等级是怎样的
- matlab线性代数对角化,工程线性代数(MATLAB版) (2007年7月)
- Android入门,android基础开发
- JMS 消息传送模式、消息签收以及spring jmsTemplate配置
- Arrayfire E0992 命令行错误: 宏定义无效: AF_<CPU/CUDA/OPENCL>
- 非淡泊无以明志,非宁静无以致远
- 【IoT】BLE 广播的基础数据定义:广播名字类型和设备类型标志
- python中图片绘制和输出相关库的原理详解
- 《web开发: Ajax 介绍》
热门文章
- 2009年9月三级网络技术51CTO版考前压轴试题
- 计算机与计算科学是属什么专业,被录取到信息与计算科学专业,这个专业什么性质,发展前景如何?...
- 磁头号和起始扇区的计算方法------软盘结构
- Yacc 与 Lex 快速入门(词法分析和语法分析)
- 已解决:Job for docker.service failed because the control process exited with error code. See “systemctl
- lua源代码分析01:lua源代码结构分析
- 简述DNS进行域名正向解析的过程。
- 4.STM32中对USART1_Config()函数的理解(自定义)
- spring12:@Component衍生注解
- mybaits三:全局配置文件(全面)