J.U.C源碼閱讀系列-TheadPoolExecutor

ThreadPoolExecutor類繼承體系:

ThreadPoolExecutor -> AbstractExecutorService -> ExecutorService -> Executor

Executors提供工廠方法累澡,用于創(chuàng)建不同類型的線程池
  • newFixedThreadPool()
  • newSingleThreadExecutor()
  • newCachedThreadPool()
  • newScheduledThreadPool()
    前兩種線程池可能隊列積壓導(dǎo)致OOM,后兩種可能創(chuàng)建非常多的線程(Integer.MAX_VALUE)碳竟,甚至OOM

以下內(nèi)容借鑒:
【鏈接】如何優(yōu)雅的關(guān)閉Java線程池
http://url.cn/5DGL2xe


線程的狀態(tài)

  • NEW
  • RUNNABLE(RUNNING)
  • BLOCKED
  • WAITING
  • TIMED_WAIT
  • TERMINATED

1. 新建(new) :新創(chuàng)建了一個線程對象译蒂。
2. 可運(yùn)行(runnable) :線程對象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對象的start()方法拿诸。該狀態(tài)的線程位于可運(yùn)行線程池中愧膀,等待被線程調(diào)度選中呛凶,獲取cpu 的使用權(quán) 旅东。
3. 運(yùn)行(running) :可運(yùn)行狀態(tài)( runnable) 的線程獲得了cpu 時間片(timeslice) 灭抑,執(zhí)行程序代碼。
4. 阻塞(block) :阻塞狀態(tài)是指線程因為某種原因放棄了cpu 使用權(quán)抵代,也即讓出了cpu timeslice腾节,暫時停止運(yùn)行。直到線程進(jìn)入 可運(yùn)行(runnable) 狀態(tài),才有機(jī)會再次獲得cpu timeslice 轉(zhuǎn)到 運(yùn)行(running) 狀態(tài)案腺。阻塞的情況分三種: (一). 等待阻塞: 運(yùn)行(running) 的線程執(zhí)行o.wait()方法庆冕,JVM會把該線程放入等待隊列(waitting queue)中。 (二). 同步阻塞: 運(yùn)行(running) 的線程在獲取對象的同步鎖時劈榨,若該同步鎖被別的線程占用愧杯,則JVM會把該線程放入鎖池(lock pool)中。 (三). 其他阻塞: 運(yùn)行(running) 的線程執(zhí)行Thread.sleep(long ms)或t.join()方法鞋既,或者發(fā)出了I/O請求時,JVM會把該線程置為阻塞狀態(tài)耍铜。當(dāng)sleep()狀態(tài)超時邑闺、join()等待線程終止或者超時、或者I/O處理完畢時棕兼,線程重新轉(zhuǎn)入 可運(yùn)行(runnable) 狀態(tài)陡舅。
5. 死亡(dead) :線程run()、main() 方法執(zhí)行結(jié)束伴挚,或者因異常退出了run()方法靶衍,則該線程結(jié)束生命周期。死亡的線程不可再次復(fù)生茎芋。


執(zhí)行任務(wù)

  • execute()
  • submit()
    可簡單理解為:前者是不需要返回值颅眶,后者需要返回值(Future.get()),雖然都是異步處理田弥。

關(guān)閉線程池

  • shutdownNow()
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();

1. 拒絕接收新提交的任務(wù)
2. 同時立刻關(guān)閉線程池涛酗,池中的任務(wù)不再執(zhí)行
3. 小結(jié):當(dāng)我們調(diào)用線程池的shutdownNow時,如果線程正在getTask方法中執(zhí)?偷厦,則會通過for循環(huán)進(jìn)入到if語句商叹,于是getTask 返回null,從而線程退出。不管線程池?是否有未完成的任務(wù)只泼。如果線程因為執(zhí)行提交到線程池里的任務(wù)而處于阻塞狀態(tài)剖笙,則會導(dǎo)致報錯。(如果任務(wù)里沒有捕獲InterruptedException異常)请唱,否則線程會執(zhí)行完當(dāng)前任務(wù)弥咪,然后通過getTask方法返回為null來退出。

  • shutdown()
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor

1. 拒絕接收新提交的任務(wù)
2. 同時等待池中的任務(wù)執(zhí)行完畢后關(guān)閉線程池
3. 小結(jié):當(dāng)我們調(diào)用線程池的shutdownNow時籍滴,如果線程正在getTask方法中執(zhí)?酪夷,則會通過for循環(huán)進(jìn)入到if語句,于是getTask 返回null,從而線程退出孽惰。不管線程池?是否有未完成的任務(wù)晚岭。如果線程因為執(zhí)行提交到線程池里的任務(wù)而處于阻塞狀態(tài),則會導(dǎo)致報錯勋功。(如果任務(wù)里沒有捕獲InterruptedException異常)坦报,否則線程會執(zhí)行完當(dāng)前任務(wù)库说,然后通過getTask方法返回為null來退出。


總結(jié)

  • shutdownNow()片择,可能會引起報錯潜的,可能會使線程池關(guān)閉不了,但不一定字管。(isTerminated=false)
  • shutdown()啰挪,要保證任務(wù)里不會有永久阻塞等待的邏輯,否則線程池也關(guān)閉不了嘲叔。(isTerminated=false)

TestCase

        // 1. 線程正在執(zhí)行時亡呵,shutdownNow不會報錯
        final ExecutorService pool1 = Executors.newSingleThreadExecutor();
        pool1.submit(() -> {
            long startTime = System.currentTimeMillis();
            System.out.println("enter the thread pool1");
            long counter = 0L;
            while (counter < Long.MAX_VALUE >> (Integer.SIZE - 4)) {
                Object o = new Object();
                o = null;
                counter++;
            }
            System.out.println("weak up from sleeping pool1");
            System.out.println("leave the thread pool1");
            long finishTime = System.currentTimeMillis();
            System.out.println("cost time " + (finishTime - startTime));
        });
        System.out.println("going to shutdown the pool1");
        pool1.shutdownNow();
        pool1.awaitTermination(1, TimeUnit.MINUTES);
        System.out.println("exit the pool1");
        boolean flag = pool1.isTerminated();
        System.out.println("pool1->" + flag);

        System.out.println("=========================");

        // 2. 線程阻塞時,shutdownNow將會拋出InterruptedException
        final ExecutorService pool2 = Executors.newSingleThreadExecutor();
        pool2.submit(() -> {
            System.out.println("enter the thread pool2");
            try {
                // 為什么兩種不同進(jìn)入阻塞狀態(tài)的實現(xiàn)硫戈,最終結(jié)果不一致
                //Thread.currentThread().wait(100_000);
                Thread.sleep(100_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("weak up from sleeping pool2");
            System.out.println("leave the thread pool2");
        });
        System.out.println("going to shutdown the pool2");
        pool2.shutdownNow();
        pool2.awaitTermination(1, TimeUnit.MINUTES);
        System.out.println("exit the pool2");
        flag = pool2.isTerminated();
        System.out.println("pool2->" + flag);

        System.out.println("=========================");

        // 3. 線程永久阻塞锰什,shutdown將無法關(guān)閉線程池
        final ExecutorService pool3 = Executors.newSingleThreadExecutor();
        pool3.submit(() -> {
            System.out.println("enter the thread pool3");
//            for (long l = 0L; l < Long.MAX_VALUE; l++) {
//                Object o = new Object();
//                o = null;
//            }
//            try {
//                Thread.sleep(1000_000_000); // 模擬永久阻塞
//                //Thread.currentThread().wait();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            try {
                ServerSocket socket = new ServerSocket(8080);
                socket.accept();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("weak up from sleeping pool3");
            System.out.println("leave the thread pool3");
        });
        System.out.println("going to shutdown the pool3");
        pool3.shutdownNow();
        pool3.awaitTermination(1, TimeUnit.MINUTES);
        System.out.println("exit the pool3");
        flag = pool3.isTerminated();
        System.out.println("pool3->" + flag);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市丁逝,隨后出現(xiàn)的幾起案子汁胆,更是在濱河造成了極大的恐慌,老刑警劉巖霜幼,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫩码,死亡現(xiàn)場離奇詭異,居然都是意外死亡罪既,警方通過查閱死者的電腦和手機(jī)谢谦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萝衩,“玉大人回挽,你說我怎么就攤上這事⌒梢辏” “怎么了千劈?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長牌捷。 經(jīng)常有香客問我墙牌,道長,這世上最難降的妖魔是什么暗甥? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任喜滨,我火速辦了婚禮,結(jié)果婚禮上撤防,老公的妹妹穿的比我還像新娘虽风。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布辜膝。 她就那樣靜靜地躺著无牵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪厂抖。 梳的紋絲不亂的頭發(fā)上茎毁,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音忱辅,去河邊找鬼七蜘。 笑死,一個胖子當(dāng)著我的面吹牛墙懂,可吹牛的內(nèi)容都是我干的崔梗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼垒在,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了扔亥?” 一聲冷哼從身側(cè)響起场躯,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旅挤,沒想到半個月后踢关,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粘茄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年签舞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柒瓣。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡儒搭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芙贫,到底是詐尸還是另有隱情搂鲫,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布磺平,位于F島的核電站魂仍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拣挪。R本人自食惡果不足惜擦酌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望菠劝。 院中可真熱鬧赊舶,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至出吹,卻和暖如春遇伞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捶牢。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工鸠珠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秋麸。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓渐排,卻偏偏與公主長得像,于是被迫代替她去往敵國和親灸蟆。 傳聞我的和親對象是個殘疾皇子驯耻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

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