Java 線程池會(huì)自動(dòng)關(guān)閉嗎?

首先我們需要了解線程池在什么情況下會(huì)自動(dòng)關(guān)閉澜公。ThreadPoolExecutor 類(這是我們最常用的線程池實(shí)現(xiàn)類)的源碼注釋中有這么一句話:

A pool that is no longer referenced in a program and has no remaining threads will be shutdown automatically.

沒有引用指向且沒有剩余線程的線程池將會(huì)自動(dòng)關(guān)閉名眉。

那么什么情況下線程池中會(huì)沒有剩余線程呢?先來(lái)看一下 ThreadPoolExecutor 參數(shù)最全的構(gòu)造方法:

/**
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 *        核心線程數(shù):即使是空閑狀態(tài)也可以在線程池存活的線程數(shù)量磕道,除非        
 *        allowCoreThreadTimeOut 設(shè)置為 true。
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 *       存活時(shí)間:對(duì)于超出核心線程數(shù)的線程行冰,空閑時(shí)間一旦達(dá)到存活時(shí)間溺蕉,就會(huì)被銷毀。
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) { ... ... }

這里我們只關(guān)心與線程存活狀態(tài)最緊密相關(guān)的兩個(gè)參數(shù)悼做,也就是corePoolSizekeepAliveTime疯特,上述代碼塊也包含了這兩個(gè)參數(shù)的源碼注釋和中文翻譯。keepAliveTime參數(shù)指定了非核心線程的存活時(shí)間肛走,非核心線程的空閑時(shí)間一旦達(dá)到這個(gè)值漓雅,就會(huì)被銷毀,而核心線程則會(huì)繼續(xù)存活朽色,只要有線程存活邻吞,線程池也就不會(huì)自動(dòng)關(guān)閉。聰明的你一定會(huì)想到葫男,如果把corePoolSize設(shè)置為0抱冷,再給keepAliveTime指定一個(gè)值的話,那么線程池在空閑一段時(shí)間之后腾誉,不就可以自動(dòng)關(guān)閉了嗎徘层?沒錯(cuò),這就是線程池自動(dòng)關(guān)閉的第一種情況利职。

1. 線程池自動(dòng)關(guān)閉的情況一:核心線程數(shù)為 0 并指定線程存活時(shí)間

1.1. 手動(dòng)創(chuàng)建線程池

代碼示例:

public class ThreadPoolTest {
    public static void main(String[] args) {
        // 重點(diǎn)關(guān)注 corePoolSize 和 keepAliveTime趣效,其他參數(shù)不重要
        ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 5,
                30L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(15));
        for (int i = 0; i < 20; i++) {
            executor.execute(() -> {
               // 簡(jiǎn)單地打印當(dāng)前線程名稱
                System.out.println(Thread.currentThread().getName());
            });
        }
    }
}

控制臺(tái)輸出結(jié)果

# 線程打印開始
... ...
pool-1-thread-2                    
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5
pool-1-thread-1
# 打印結(jié)束,程序等待30s后正常退出
Process finished with exit code 0   # 小知識(shí):exit code 0 說(shuō)明程序是正常退出猪贪,非強(qiáng)行中斷或異常退出

通過以上代碼和運(yùn)行結(jié)果可以得知跷敬,在corePoolSize為0且keepAliveTime設(shè)置為 60s 的情況下,如果任務(wù)執(zhí)行完畢又沒有新的任務(wù)到來(lái)热押,線程池里的線程都將消亡西傀,而且沒有核心線程阻止線程池關(guān)閉,因此線程池也將隨之自動(dòng)關(guān)閉桶癣。

而如果將corePoolSize設(shè)置為大于0的數(shù)字拥褂,再運(yùn)行以上代碼,那么線程池將一直處于等待狀態(tài)而不能關(guān)閉牙寞,因?yàn)楹诵木€程不受keepAliveTime控制饺鹃,所以會(huì)一直存活,程序也將一直不能結(jié)束间雀。運(yùn)行效果如下 (corePoolSize設(shè)置為5悔详,其他參數(shù)不變)

控制臺(tái)輸出結(jié)果

# 線程打印開始
... ...
pool-1-thread-5
pool-1-thread-1
pool-1-thread-3
pool-1-thread-4
pool-1-thread-2
# 打印結(jié)束,但程序無(wú)法結(jié)束

2.2 Executors.newCachedThrteadPool() 創(chuàng)建線程池

ExecutorsJDK 自帶的線程池框架類惹挟,包含多個(gè)創(chuàng)建不同類型線程池的方法茄螃,而其中的newCachedThrteadPool()方法也將核心線程數(shù)設(shè)置為了0并指定了線程存活時(shí)間,所以也可以自動(dòng)關(guān)閉连锯。其源碼如下:

public class Executors {
    ... ...
    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
    }
    ... ...
}

如果用這個(gè)線程池運(yùn)行上面的代碼归苍,程序也會(huì)自動(dòng)退出,效果如下

# 線程打印開始
... ...
pool-1-thread-7
pool-1-thread-5
pool-1-thread-4
pool-1-thread-1
pool-1-thread-9
# 打印結(jié)束运怖,程序等待60s后退出
Process finished with exit code 0

2. 線程池自動(dòng)關(guān)閉的情況二:通過 allowCoreThreadTimeOut 控制核心線程存活時(shí)間

通過將核心線程數(shù)設(shè)置為0雖然可以實(shí)現(xiàn)線程池的自動(dòng)關(guān)閉霜医,但也存在一些弊端,此話怎講驳规,先來(lái)看一下線程池的執(zhí)行流程:

線程池執(zhí)行流程圖

如圖所示肴敛,當(dāng)有新的任務(wù)到來(lái)時(shí),程序會(huì)先判斷線程池當(dāng)前線程數(shù)是否達(dá)到corePoolSize(核心線程數(shù))吗购,沒達(dá)到則創(chuàng)建線程執(zhí)行任務(wù)医男,達(dá)到則嘗試將任務(wù)放入任務(wù)隊(duì)列 (workQueue)。如果將corePoolSize設(shè)置為0的話捻勉,新到來(lái)的任務(wù)會(huì)永遠(yuǎn)優(yōu)先被放入任務(wù)隊(duì)列镀梭,然后等待被處理,這顯然會(huì)影響程序的執(zhí)行效率踱启。那你可能要問了报账,有沒有其他的方法來(lái)自己實(shí)現(xiàn)可自動(dòng)關(guān)閉的線程池呢研底?答案是肯定的,從 JDK 1.6 開始透罢,ThreadPoolExecutor 類新增了一個(gè)allowCoreThreadTimeOut字段:

/**
 * If false (default), core threads stay alive even when idle.
 * If true, core threads use keepAliveTime to time out waiting
 * for work.
 * 默認(rèn)為false榜晦,核心線程處于空閑狀態(tài)也可一直存活
 * 如果設(shè)置為true,核心線程的存活狀態(tài)將受keepAliveTime控制羽圃,超時(shí)將被銷毀
 */
private volatile boolean allowCoreThreadTimeOut;

這個(gè)字段值默認(rèn)為false乾胶,可使用allowCoreThreadTimeOut()方法對(duì)其進(jìn)行設(shè)置,如果設(shè)置為 true朽寞,那么核心線程數(shù)也將受keepAliveTime控制识窿,此方法源碼如下:

public void allowCoreThreadTimeOut(boolean value) {
    // 核心線程存活時(shí)間必須大于0,一旦開啟脑融,keepAliveTime 也必須大于0
    if (value && keepAliveTime <= 0)
        throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
    // 將 allowCoreThreadTimeOut 值設(shè)為傳入的參數(shù)值
    if (value != allowCoreThreadTimeOut) {
        allowCoreThreadTimeOut = value;
        // 開啟后喻频,清理所有的超時(shí)空閑線程,包括核心線程
        if (value)
            interruptIdleWorkers();
    }
}

既然如此肘迎,接下來(lái)我們就借助這個(gè)方法實(shí)現(xiàn)一個(gè)可自動(dòng)關(guān)閉且核心線程數(shù)不為0的線程池半抱,這里直接在第一個(gè)程序的基礎(chǔ)上進(jìn)行改進(jìn):

public class ThreadPoolTest {
    public static void main(String[] args) {
        // 這里把corePoolSize設(shè)為5,keepAliveTime保持不變
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,
                30L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(15));
        // 允許核心線程超時(shí)銷毀
        executor.allowCoreThreadTimeOut(true);
        for (int i = 0; i < 20; i++) {
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName());
            });
        }
    }
}

運(yùn)行結(jié)果

# 線程打印開始
... ...
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5
# 打印結(jié)束膜宋,程序等待30s后退出
Process finished with exit code 0

可以看到窿侈,程序在打印結(jié)束后等待了30s,然后自行退出秋茫,說(shuō)明線程池已自動(dòng)關(guān)閉史简,也就是allowCoreThreadTimeOut()方法發(fā)揮了作用。這樣肛著,我們就實(shí)現(xiàn)了可自動(dòng)關(guān)閉且核心線程數(shù)不為0的線程池圆兵。

3. 超詳細(xì)的線程池執(zhí)行流程圖

讓我們?cè)賮?lái)梳理一下更完整的線程池執(zhí)行流程。

更完整的線程池執(zhí)行流程圖

4. 結(jié)語(yǔ)

以上就是線程池可以自動(dòng)關(guān)閉的兩種情況枢贿,而且梳理了詳細(xì)的線程池執(zhí)行流程殉农,相信你看完本文一定會(huì)有所收獲。不過話又說(shuō)回來(lái)局荚,可自動(dòng)關(guān)閉的線程池的實(shí)際應(yīng)用場(chǎng)景并不多超凳,更多時(shí)候需要我們手動(dòng)關(guān)閉。下一篇文章我們就來(lái)聊聊如何手動(dòng)關(guān)閉線程池耀态,敬請(qǐng)期待轮傍!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市首装,隨后出現(xiàn)的幾起案子创夜,更是在濱河造成了極大的恐慌,老刑警劉巖仙逻,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驰吓,死亡現(xiàn)場(chǎng)離奇詭異涧尿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)檬贰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門姑廉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人偎蘸,你說(shuō)我怎么就攤上這事庄蹋∷材冢” “怎么了迷雪?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)虫蝶。 經(jīng)常有香客問我章咧,道長(zhǎng),這世上最難降的妖魔是什么能真? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任赁严,我火速辦了婚禮,結(jié)果婚禮上粉铐,老公的妹妹穿的比我還像新娘疼约。我一直安慰自己,他們只是感情好蝙泼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布程剥。 她就那樣靜靜地躺著,像睡著了一般汤踏。 火紅的嫁衣襯著肌膚如雪织鲸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天溪胶,我揣著相機(jī)與錄音搂擦,去河邊找鬼。 笑死哗脖,一個(gè)胖子當(dāng)著我的面吹牛瀑踢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播才避,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼丘损,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了工扎?” 一聲冷哼從身側(cè)響起徘钥,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肢娘,沒想到半個(gè)月后呈础,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舆驶,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年而钞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沙廉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臼节,死狀恐怖撬陵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情网缝,我是刑警寧澤巨税,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站粉臊,受9級(jí)特大地震影響草添,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扼仲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一远寸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧屠凶,春花似錦驰后、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至贱枣,卻和暖如春监署,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纽哥。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工钠乏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人春塌。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓晓避,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親只壳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子俏拱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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