當前位置:
首頁 > 知識 > 線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

線程池

上文參看《線程池ThreadPoolExecutor里Control state,ctl參數的分析(一)》

先說一下線程池的5個狀態:

RUNNING:可以接收新的任務,處理隊列中的任務。這是線程池的初始化狀態。

SHUTDOWN:不再接收新的任務,但是會處理隊列中的任務。

STOP:不再接收新的任務,也不再處理隊列中的任務,並且會去中斷正在運行中的任務。

TIDYING:所有任務都已經終止,workerCount有效線程數為0,線程池進入本狀態,開始運行鉤子函數terminated()。這段代碼是空的:

protected void terminated() { }

如果開發人員想在TIDYING狀態下做點處理,子類可以重寫這個方法。

像terminated()這樣專供子類重寫的方法共有三個:

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

鉤子函數

beforeExecute:當線程池正要開始執行某個任務的時候(注意不是任務進入等待隊列的時候,是將要開始正式在線程池中執行的時候),線程池會觸發這個方法的調用。

afterExecute:當線程池完成了某一個任務的執行後,線程池就會觸發這個方法。

terminated:當線程池本身停止執行的時候,該方法就會被調用。

TERMINATED:徹底終結。好懷念「YOU ARE TERMINATED」~~

狀態的轉換如下圖:

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

狀態轉換

5種狀態對應的值是-1,0,1,2,3,上文我們也說到,二進位中,只需要用前3位即可表達,所以對應如下:

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

數值轉換

整個32位的分配我們明白了,高3位是狀態,低29位是有效線程數。那麼ThreadPoolExecutor是如何將這兩個值裝配到ctl內,又如何從ctl中獲取狀態、線程數呢?

在談這幾個問題之前,我們複習一下基礎課,Java的各種運算符。有一些在《Java面試題 Part16 手寫HashMap(一)》那篇文章中寫了,大家可以一併看。

上文談原反補碼的時候忘了說了,大家都知道的,Java用的是補碼,正數的原反補碼是一模一樣的,負數則按照規則進行運算,也就是說你看到首位是0,正數,那麼就可以直接拿著補碼當原碼進行換算,如果你看到首位是1,負數,看到的這個是補碼,就需要-1,取反才能得出原碼,進行換算。


<<左移:

2的二進位10,<<1左移一位,即100,即4。



~位非:

就是按位取反,0變成1,1變成0。

-2是 11111111111111111111111111111110(補碼,有興趣的同學可以試著轉成原碼)

~-2是00000000000000000000000000000001(也是補碼,但正數的原反補碼相同),即1。

2是 00000000000000000000000000000010

~2是11111111111111111111111111111101,即-3。



&位與:

從高位開始比較,二者都為1,結果為1,有一個為0,結果為0。

12是 1100

9是 1001

12&9是 1000,是8。



|位或:

從高位開始比較,二者有一個為1,結果為1,反之都為0,結果為0。

12是 1100

9是 1001

12|9是 1101,是13。

基礎複習完畢,接著談對ctl的操作。

第一個就是如何打包ctl,畢竟是把兩個數裝配成一個。

上文我們說了,狀態值是-1到3,左移29位,將其存儲在高位,也就是:

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

狀態值轉換

COUNT_BITS是29。

我們不用管真實的INT值是多少,只需要知道這5個值的二進位只有前3位有用,後面的全是0。

上文也說到,線程數放在後29位里,最大值也就是00011111111111111111111111111111,所以不管線程數是多少,前3位必是000。



那麼如何將runState和workerCount裝配起來呢?

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

打包ctl

rs就是runState,wc就是workerCount。舉個例子,狀態是RUNNING,線程數是2,

ctl(RUNNING,2),如何裝配呢?

RUNNING:11100000000000000000000000000000

2 :00000000000000000000000000000010

|位或 :11100000000000000000000000000010

至於位或出來的INT值是多少,就不重要了,重要的是通過|位或,將前三位的狀態與後29位的數值組合起來了。



能打包就能拆包,我們要從打包好的ctl里分別獲取runState和workerCount。

獲取狀態值:

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

獲取狀態值

c就是打包好的ctl,CAPACITY就是線程數的最大量00011111111111111111111111111111。

取runState,其實就是取ctl的前3位,不管ctl是多少,後面的29位統一變成0就行了。

第一步:~CAPACITY,就是把 00011111111111111111111111111111

變成 11100000000000000000000000000000

第二步:ctl&~CAPACITY,ctl具體的值是多少無所謂,我們只需要關注前三位即可,假設是01100000000000000000000000000010&11100000000000000000000000000000。

&二者比較,有一個為0,結果為0,因為~CAPACITY,後面29位全變成了0,所以ctl與之&,後面的29位一定為0,而前3位,二者比較,都為1,結果為1,所以例子上011與111相比,得到011。最後的結果就是純潔的,不含線程數的狀態值。

獲取線程數:

線程池ThreadPoolExecutor里Control state,ctl參數的分析(二)

獲取線程數

只需要把ctl的前三位變成000就行了,例如:

01100000000000000000000000000010&00011111111111111111111111111111

結果就是00000000000000000000000000000010。



終於寫完了,感慨一下,本來只是想看看線程池的代碼,哪想到看第一行代碼就被ctl這個參數迷住了,百思不得其解怎麼就能用1個數字表達兩個概念,就想琢磨透它,連看帶測試帶寫文章,三天就過去了。

唯一的感慨:神就是神。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Java個人學習心得 的精彩文章:

線程池ThreadPoolExecutor里Control state,ctl參數的分析(一)

TAG:Java個人學習心得 |