【多线程】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 = 15ThreadPoolExecutor 源碼分析
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 1111runState:線程池里線程的運行狀態
正數的原碼、反碼、補碼都是一樣的,在計算機底層,是用補碼來表示的
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 0000SHUTDOWN 關閉狀態
不接受新任務,處理已經在阻塞隊列的任務,高3位全是0,就是SHUTDOWN狀態
0 << COUNT_BITS0的表示00000000 00000000 00000000 00000000往左移29位00000000 00000000 00000000 00000000STOP
不接受新任務,也不處理阻塞隊列里的任務,并且會中斷正在處理的任務,所以高3位是001,就是STOP狀態
1 << COUNT_BITS1的表示00000000 00000000 00000000 00000001往左移29位00100000 00000000 00000000 00000000TIDYING
所有任務都被中止,workerCount是0,線程狀態轉化為TIDYING并且調用terminated()鉤子方法,所以高3位是010,就是TIDYING狀態
2 << COUNT_BITS2的32位2進制00000000 00000000 00000000 00000010往左移29位01000000 00000000 00000000 00000000TERMINATED
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,這也就獲得了線程池的運行狀態runStateworkerCountOf
獲取線程池的當前有效線程數目
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类源码解析----续(二进制相关运算)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【多线程】ThreadPoolExecu
- 下一篇: 【多线程】线程池拒绝策略详解与自定义拒绝