看ThreadPoolExecutor源碼前的騷操作

最近一個月看了學習了很多關于SQL性能優(yōu)化、Spring核心源碼分析、MyBatis核心源碼分析、JUC并發(fā)包下面的知識點,感覺收獲很多。這幾天,會陸陸續(xù)續(xù)產(chǎn)出一些博客,進行知識總結(jié)。一邊健忘一邊學習新知識點,痛苦并快樂著。
saoqi.png

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.符號位和其他有效值一起進行處理。計算機不適合做減法,用加法代替減法?!綼 - b】補碼= 【a】補碼+ 【-b】補碼,這里值得注意的是,有的計算機中是有乘法器的,有的計算機是用加法代替乘法。

有趣的例子

看到下面java代碼,結(jié)果是多少呢? 答案:-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進行按位取反(包括最高位),取反后的結(jié)果應該是11111111111111111111111111111111。我們可以發(fā)現(xiàn)這是一個負數(shù),負數(shù)的反碼 = 補碼 - 1,我們計算出反碼 = 11111111111111111111111111111110。我們對反碼進行按位取反(除了最高位),結(jié)果是10000000000000000000000000000001。最后原碼進行二進制到十進制的轉(zhuǎn)換,結(jié)果是 -1。

想必有了這些基礎,再去分析ThreadPoolExecutor源碼中的成員變量,應該會輕松很多。

回去分析源碼

 private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

1的補碼是00000000000000000000000000000001,向左移動29個位置,應該變成0010000000000000000000000000000。然后再減去 - 1,通過前面的基礎,我們知道-1的補碼是11111111111111111111111111111111。2個數(shù)值相加的結(jié)果是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位的計算結(jié)果都是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。

進行總結(jié)

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(寓意是沉夢昂志的小馬),希望和你們一起成長進步,感謝各位閱讀本文章。

如果您對這篇文章有什么意見或者錯誤需要改進的地方,歡迎與我討論。
如果您覺得還不錯的話,希望你們可以點個贊。
希望我的文章對你能有所幫助。
有什么意見、見解或疑惑,歡迎留言討論。

最后送上:心之所向,素履以往。生如逆旅,一葦以航。


saoqi.png
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容