Flag
ThreadPoolExecutor(線程池)身冬,大家使用線程的時候,都用過它對線程進行創(chuàng)建及其調(diào)度管理岔乔,想必再熟悉不過酥筝。
當我們點開ThreadPoolExecutor源碼,看到這一幕雏门,大多數(shù)人第一感覺會跟我一樣:wtf嘿歌,這是什么東西。其實仔細看一下茁影,不難理解宙帝。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
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; }
AtomicInteger這個類是基于CAS,是解決在高并發(fā)情況下原生的int類型自增線程不安全的問題募闲。AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
ctl代表ThreadPoolExecutor中的控制狀態(tài)步脓。默認情況下ctl代表是RUNNING狀態(tài)。
int類型占32位,顯而易見Integer.size等于32。那么COUNT_BITS等于29涉瘾。再看到(1 << COUNT_BITS) - 1
這一行,說明正數(shù)1向左邊移動了29位鸳君。想必看到這里,大學學的原碼厨幻,補碼相嵌,反碼腿时,十進制與二進制况脆,運算符都忘光了吧。我們來回顧一下運算符的知識點批糟。
運算符
-
Java
支持的位運算符有7
個:-
&
:按位與格了。當兩位同時為1
時才返回1
。 -
|
:按位或徽鼎。只要有一位為1
即可返回1
盛末。 -
~
:按位非。單目運算符否淤,將操作數(shù)的每個位(包括符號位)全部取反悄但。 -
^
:按位異或。當兩位相同時返回0
石抡,不同時返回1
檐嚣。 -
<<
:左移運算符。 -
>>
:右移運算符啰扛。 -
>>>
:無符號位右移運算符嚎京。比如-5>>>2
嗡贺,-5
無符號右移動2
位后,左邊空出2
位后鞍帝,空出來的2
位用0
去補充诫睬。
-
原碼,反碼帕涌,補碼
在計算機中摄凡,數(shù)據(jù)都以補碼的形式存在的,數(shù)據(jù)在計算機中都是以二進制存在的宵膨。計算機存儲數(shù)據(jù)是以字節(jié)為單位架谎。
無符號數(shù)來說沒有原碼,反碼辟躏,補碼之分谷扣,因為都相同。正數(shù)的原碼捎琐,反碼会涎,補碼都相同。比如+0的原碼是00000000瑞凑,補碼是00000000末秃,補碼是00000000。(計算機字長為8)
負數(shù)的反碼是對該負數(shù)的原碼(除了符號位)進行按位取反籽御。比如-0的原碼是10000000练慕,反碼是11111111(計算機字長為8)
負數(shù)的補碼 = 負數(shù)的反碼 + 1,比如-0的反碼是11111111技掏,在最后一位進行加1铃将,就變成了00000000。即-0的補碼為00000000
記住計算機規(guī)則都是滿則進一位哑梳。
計算機中為什么使用補碼來存儲數(shù)據(jù)
1.看到上面的例子就懂了劲阎。+0的原碼和反碼和-0的原碼和反碼都不一樣,而它們的補碼都是一樣的鸠真。就是解決了這種不一致的問題悯仙。
2.符號位和其他有效值一起進行處理。計算機不適合做減法吠卷,用加法代替減法锡垄。【a - b】補碼= 【a】補碼+ 【-b】補碼祭隔,這里值得注意的是货岭,有的計算機中是有乘法器的,有的計算機是用加法代替乘法。
有趣的例子
看到下面java代碼茴她,結果是多少呢寻拂? 答案:-1。首先我們要記住一點就是丈牢,-1在計算中補碼表示每位都是1祭钉。java中,-1的二進制表示是11111111111111111111111111111111己沛,也就是32個1慌核。
/**
* @author cmazxiaoma
* @version V1.0
* @Description: TODO
* @date 2018/8/16 20:01
*/
public class Test {
public static void main(String[] args) {
System.out.println(~0);
}
}
0的補碼是00000000000000000000000000000000,~0
的意思對0進行按位取反(包括最高位)申尼,取反后的結果應該是11111111111111111111111111111111垮卓。我們可以發(fā)現(xiàn)這是一個負數(shù),負數(shù)的反碼 = 補碼 - 1师幕,我們計算出反碼 = 11111111111111111111111111111110粟按。我們對反碼進行按位取反(除了最高位),結果是10000000000000000000000000000001霹粥。最后原碼進行二進制到十進制的轉(zhuǎn)換灭将,結果是 -1。
想必有了這些基礎后控,再去分析ThreadPoolExecutor源碼中的成員變量庙曙,應該會輕松很多。
回去分析源碼
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
1的補碼是00000000000000000000000000000001浩淘,向左移動29個位置捌朴,應該變成0010000000000000000000000000000。然后再減去 - 1张抄,通過前面的基礎砂蔽,我們知道-1的補碼是11111111111111111111111111111111。2個數(shù)值相加的結果是00011111111111111111111111111111欣鳖。即capacity的值察皇,我們會發(fā)現(xiàn)高3位都是0茴厉,低29位都是1泽台。
通過一系列的運算,我們可以計算出矾缓。
RUNNING = 11100000000000000000000000000000
SHUTDOWN = 00000000000000000000000000000000
STOP = 0010000000000000000000000000000
TIDYING = 0100000000000000000000000000000
TERMINATED = 0110000000000000000000000000000
按數(shù)值從小到大排序就是RUNNING怀酷、SHUTDOWN、STOP嗜闻、TIDYING蜕依、TERMINATE
private static int workerCountOf(int c) { return c & CAPACITY; }
我們計算workerCount的值是通過c & CAPACITY
,也就是按位與。明顯是capacity的前3位是000,與c的前3位進行按位與的話样眠,也都000友瘤。所以workerCount占據(jù)ctl變量的低29位。
private static int runStateOf(int c) { return c & ~CAPACITY; }
我們計算runState的值是通過先將capacity進行按位非的操作檐束,然后再和c進行按位與的操作~capacity
后的值高3位是111辫秧,低29位都是0。c的低29位和~capacity
的低29位的計算結果都是0被丧,可以忽略盟戏。實際參與計算的是~capacity
是高3位(都是1)和c的高3位。所以說runState占據(jù)ctl變量的高3位甥桂。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }
我們看到ctl初始化方法柿究, 現(xiàn)在很輕松的知道了,runstate為Running狀態(tài)黄选,workCount等于0蝇摸。
進行總結
workerCount 線程池中當前活動的線程數(shù)量,占據(jù)ctl變量的低29位办陷。
runState 是線程池運行狀態(tài)探入,占據(jù)ctl變量的高3位。有5種狀態(tài):
- RUNNING:接收新的任務懂诗,并且隊列中的任務蜂嗽。
- SHUTDOWN:不接收新的任務,但是會處理隊列中的任務和正在運行中的任務殃恒。
- STOP:不接收新的任務植旧,也不處理隊列中的任務,中斷正在執(zhí)行中的任務离唐。
- TIDYING:所有任務都已經(jīng)終止病附,workerCount等于0,將運行terminated()方法
- TERMINATED:terminated()方法執(zhí)行完畢亥鬓。
我們還要明白線程池運行狀態(tài)的轉(zhuǎn)換
- running 到shutdown的過程:顯式調(diào)用shutdown()方法完沪,或者在線程池中調(diào)用finalize方法。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
/**
* Invokes {@code shutdown} when this executor is no longer
* referenced and it has no threads.
*/
protected void finalize() {
shutdown();
}
- shutdown到stop的過程:調(diào)用了shutdownNow()方法
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
- shutdown到tidying的過程:線程池和任務隊列都為空嵌戈。
- stop到tidying的過程:線程池為空覆积。
- terminated的過程: 調(diào)用terminated()方法。
/**
* Method invoked when the Executor has terminated. Default
* implementation does nothing. Note: To properly nest multiple
* overridings, subclasses should generally invoke
* {@code super.terminated} within this method.
*/
protected void terminated() { }
尾言
大家好熟呛,我是cmazxiaoma(寓意是沉夢昂志的小馬)宽档,希望和你們一起成長進步,感謝各位閱讀本文章庵朝。
如果您對這篇文章有什么意見或者錯誤需要改進的地方,歡迎與我討論吗冤。
如果您覺得還不錯的話,希望你們可以點個贊又厉。
希望我的文章對你能有所幫助。
有什么意見椎瘟、見解或疑惑覆致,歡迎留言討論。
最后送上:心之所向肺蔚,素履以往篷朵。生如逆旅,一葦以航婆排。