ThreadPoolExecutor關閉線程池詳解

概述

在之前的一篇博客里談談ThreadPoolExecutor的實現(xiàn)已經對ThreadPoolExecutor中的線程如何運行進行了簡單的介紹值依,本文將介紹線程池是如何進行結束的,并對上篇文章遺留問題進行解答颇蜡。

功能介紹

java中線程池提供了兩個關閉方法shutdown和shutdownNow辆亏,兩個方法的具體使用如下:

//執(zhí)行該方法,線程處于shutdown狀態(tài)扮叨,線程池不允許再提交任務,但是已提交的任務會繼續(xù)執(zhí)行直到結束
void shutdown();
//執(zhí)行該方法甸鸟,線程會處于stop狀態(tài)兵迅,線程池會試圖停止正在執(zhí)行的任務,并返回沒有執(zhí)行成功的任務列表
List<Runnable> shutdownNow();

源碼分析

我們知道刻恭,ThreadPoolExecutor在會將每個線程封裝為一個Worker對象,該對象會持有任務并且在執(zhí)行完其創(chuàng)建時的第一個任務firstTask后鳍贾,會阻塞的從workQueue中獲取任務。前面我們講到shutdown方法會將已提交的任務執(zhí)行直到結束橡淑,同時會將空閑線程回收(可以先思考下咆爽,如何判斷線程是否空閑?)斗埂。我們進入源碼進行分析:

public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
       //全局加鎖,保證只會有一個線程內執(zhí)行該方法
        mainLock.lock();
        try {
           //權限檢查男娄,忽略
            checkShutdownAccess();
            //將線程池狀態(tài)置為SHUTDOWN
            advanceRunState(SHUTDOWN);
            //中斷空閑線程
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
 }
private void interruptIdleWorkers() {
        interruptIdleWorkers(false);
 }
private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //遍歷每個Worker對象
            for (Worker w : workers) {
                Thread t = w.thread;
                //如果沒有被中斷漾稀,并且持有Worker的互斥鎖,說明該woker對象為空閑線程并且沒有被中斷
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        //中斷worker對象持有的線程尸折,因為該線程可能正在阻塞在任務隊列中
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }

我們可以回到我之前的博客談談ThreadPoolExecutor的實現(xiàn)查看Worker類在執(zhí)行任務之前會首先獲取到自身的互斥鎖缕贡,這樣如果獲取不到Worker的互斥鎖,則說明該worker正在執(zhí)行任務收擦,這就回答了我們上面的問題塞赂,也是為什么Worker類實現(xiàn)AQS的原因(這里并不是為了并發(fā)安全昼蛀,只是為了判斷線程是否正在執(zhí)行任務叼旋,下面會有更深刻的認識)。當我們在主線程中中斷了worker中持有的線程夫植,woker線程中的runWorker方法會執(zhí)行最外圍的processWorkerExit函數銷毀線程油讯,進而完全結束worker的生命周期延欠。

而正在執(zhí)行的線程在執(zhí)行完其任務也會因為獲取不到任務進入processWorkerExit函數,結束線程生命周期兔综。

下面狞玛,我們繼續(xù)看下shutdownNow方法如何實現(xiàn)的,相比于shutdown方法为居,該方法簡直太狠了直接對所有的線程執(zhí)行中斷杀狡,具體代碼如下:

public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            //權限檢查
            checkShutdownAccess();
            //將線程池狀態(tài)置為STOP
            advanceRunState(STOP);
            //中斷所有線程包括
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
}
//這個函數就比較狠了,無論是否持有鎖膳凝,只要線程沒有被中斷恭陡,就中斷Worker持有的線程
private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
}
private void interruptWorkers() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
           //循環(huán)遍歷中斷線程
            for (Worker w : workers)
                w.interruptIfStarted();
        } finally {
            mainLock.unlock();
        }
}
void interruptIfStarted() {
            Thread t;
            //無論是否持有互斥鎖,只有線程沒有被中斷就執(zhí)行interrupt
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
  }

使用建議

在日常開發(fā)中著淆,我們一般不會去使用shutdownNow拴疤,這個方法會導致部分任務無法執(zhí)行。我們通常會調用shutdown方法使線程池不接受新的任務苔埋,然后等正在執(zhí)行的任務執(zhí)行完成后再結束蜒犯。下面我給出一個個人覺得比較優(yōu)雅的結束線程池的使用示例:

public class ThreadPoolExecutorTest {
    private static Logger logger = LoggerFactory.getLogger(ThreadPoolExecutorTest.class);

    private static ExecutorService executorService = Executors.newFixedThreadPool(3);

    public static void main(String []args) {
        for (int i = 0; i< 5; i++) {
            executorService.submit(new SleepRunnable());
        }
        executorService.shutdown();//執(zhí)行該方法相當于通知線程池結束
        try {
           //阻塞等待線程池結束
            executorService.awaitTermination(1000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            //出現(xiàn)異常是強制結束
            List<Runnable> notExcuteRunnables = executorService.shutdownNow();
            logger.info("awaitTermination exception, notExcuteRunnables = {}", notExcuteRunnables, e);
        }
    }

    //測試任務,簡單的sleep 1s
    static class SleepRunnable implements Runnable {

        @Override
        public void run() {
            try {
                logger.info("sleep in thread = {}", Thread.currentThread().getName());
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                logger.info("exception in sleep", e);
            }
        }
    }
}

原文

袁瓊瓊的技術博客玉工,歡迎指針
http://yuanqiongqiong.cn/2019/07/15/ThreadPoolExecutor%E5%85%B3%E9%97%AD%E7%BA%BF%E7%A8%8B%E6%B1%A0%E8%AF%A6%E8%A7%A3/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末淘菩,一起剝皮案震驚了整個濱河市遵班,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖费奸,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弥激,死亡現(xiàn)場離奇詭異,居然都是意外死亡愿阐,警方通過查閱死者的電腦和手機微服,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缨历,“玉大人以蕴,你說我怎么就攤上這事⌒练酰” “怎么了丛肮?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵魄缚,是天一觀的道長宝与。 經常有香客問我,道長冶匹,這世上最難降的妖魔是什么习劫? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮嚼隘,結果婚禮上诽里,老公的妹妹穿的比我還像新娘。我一直安慰自己飞蛹,他們只是感情好谤狡,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卧檐,像睡著了一般墓懂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泄隔,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天拒贱,我揣著相機與錄音,去河邊找鬼佛嬉。 笑死逻澳,一個胖子當著我的面吹牛,可吹牛的內容都是我干的暖呕。 我是一名探鬼主播斜做,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼湾揽!你這毒婦竟也來了瓤逼?” 一聲冷哼從身側響起笼吟,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎霸旗,沒想到半個月后贷帮,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡诱告,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年撵枢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片精居。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡锄禽,死狀恐怖,靈堂內的尸體忽然破棺而出靴姿,到底是詐尸還是另有隱情沃但,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布佛吓,位于F島的核電站宵晚,受9級特大地震影響,放射性物質發(fā)生泄漏辈毯。R本人自食惡果不足惜坝疼,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一搜贤、第九天 我趴在偏房一處隱蔽的房頂上張望谆沃。 院中可真熱鬧,春花似錦仪芒、人聲如沸唁影。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽据沈。三九已至,卻和暖如春饺蔑,著一層夾襖步出監(jiān)牢的瞬間锌介,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工猾警, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留孔祸,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓发皿,卻偏偏與公主長得像崔慧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子穴墅,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345