看ThreadPoolExecutor源碼前的騷操作

最近一個月看了學習了很多關于SQL性能優(yōu)化菌湃、Spring核心源碼分析律想、MyBatis核心源碼分析配猫、JUC并發(fā)包下面的知識點尤辱,感覺收獲很多。這幾天先嬉,會陸陸續(xù)續(xù)產(chǎn)出一些博客轧苫,進行知識總結。一邊健忘一邊學習新知識點,痛苦并快樂著含懊。
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.符號位和其他有效值一起進行處理。計算機不適合做減法吠卷,用加法代替減法锡垄。【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(寓意是沉夢昂志的小馬)宽档,希望和你們一起成長進步,感謝各位閱讀本文章庵朝。

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

最后送上:心之所向肺蔚,素履以往篷朵。生如逆旅,一葦以航婆排。


saoqi.png
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末声旺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子段只,更是在濱河造成了極大的恐慌腮猖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赞枕,死亡現(xiàn)場離奇詭異澈缺,居然都是意外死亡,警方通過查閱死者的電腦和手機炕婶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進店門姐赡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柠掂,你說我怎么就攤上這事项滑。” “怎么了涯贞?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵枪狂,是天一觀的道長。 經(jīng)常有香客問我宋渔,道長州疾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任皇拣,我火速辦了婚禮严蓖,結果婚禮上,老公的妹妹穿的比我還像新娘氧急。我一直安慰自己颗胡,他們只是感情好,可當我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布态蒂。 她就那樣靜靜地躺著杭措,像睡著了一般费什。 火紅的嫁衣襯著肌膚如雪钾恢。 梳的紋絲不亂的頭發(fā)上手素,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機與錄音瘩蚪,去河邊找鬼泉懦。 笑死,一個胖子當著我的面吹牛疹瘦,可吹牛的內(nèi)容都是我干的崩哩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼言沐,長吁一口氣:“原來是場噩夢啊……” “哼邓嘹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起险胰,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤汹押,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后起便,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棚贾,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年榆综,在試婚紗的時候發(fā)現(xiàn)自己被綠了妙痹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡鼻疮,死狀恐怖怯伊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情判沟,我是刑警寧澤震贵,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站水评,受9級特大地震影響猩系,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜中燥,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一寇甸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疗涉,春花似錦拿霉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至闹伪,卻和暖如春沪铭,著一層夾襖步出監(jiān)牢的瞬間壮池,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工杀怠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留椰憋,地道東北人。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓赔退,卻偏偏與公主長得像橙依,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子硕旗,可洞房花燭夜當晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容