【搞定面試官】你還在用Executors來創(chuàng)建線程池?會(huì)有什么問題呢询刹?

前言

上文我們介紹了JDK中的線程池框架Executor谜嫉。我們知道,只要需要?jiǎng)?chuàng)建線程的情況下范抓,即使是在單線程模式下骄恶,我們也要盡量使用Executor。即:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(1); 
//此處不該利用Executors工具類來初始化線程池

但是匕垫,在《阿里巴巴Java開發(fā)手冊(cè)》中有一條

【強(qiáng)制】線程池不允許使用 Executors 去創(chuàng)建僧鲁,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則象泵,規(guī)避資源耗盡的風(fēng)險(xiǎn)寞秃。

Executors 返回的線程池對(duì)象的弊端如下:
FixedThreadPool 和 SingleThreadPool : 允許的請(qǐng)求隊(duì)列長(zhǎng)度為 Integer.MAX_VALUE,可能會(huì)堆積大量的請(qǐng)求偶惠,從而導(dǎo)致 OOM春寿。
CachedThreadPool 和 ScheduledThreadPool : 允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE,可能會(huì)創(chuàng)建大量的線程忽孽,從而導(dǎo)致 OOM绑改。

可以看到谢床,這是一個(gè)強(qiáng)制性的規(guī)則,并且是不允許使用Executors來創(chuàng)建厘线,建議使用ThreadPoolExecutor來創(chuàng)建線程池识腿,那我們先來回顧一下ExecutorsThreadPoolExecutor

我們可以看到ThreadPoolExecutor已經(jīng)是Executor的具體實(shí)現(xiàn)了造壮,而且具有較多可配參數(shù)(可配參數(shù)見下方渡讼,可僅了解,用到時(shí)再進(jìn)行詳細(xì)查詢)耳璧。Executors是一個(gè)創(chuàng)建線程池的工具類成箫,查看其源碼的話也會(huì)發(fā)現(xiàn)這幾種創(chuàng)建線程池的方法也都是通過調(diào)用ThreadPoolExecutor來實(shí)現(xiàn)的。

ThreadPoolExecutor一共有四個(gè)構(gòu)造函數(shù)旨枯,七個(gè)可配參數(shù)蹬昌,分別是

  1. corePoolSize: 線程池中保持存活線程的數(shù)量。
  2. maximumPoolSize: 線程池中允許線程數(shù)量的最大值
  3. keepAliveTime: 表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止
  4. unit: 參數(shù)keepAliveTime的時(shí)間單位
  5. workQueue: 一個(gè)阻塞隊(duì)列攀隔,用來存儲(chǔ)等待執(zhí)行的任務(wù)
  6. threadFactory: 線程工廠凳厢,主要用來創(chuàng)建線程
  7. handler:表示當(dāng)拒絕處理任務(wù)時(shí)的策略

分析

那么Executors到底會(huì)導(dǎo)致什么問題,才會(huì)讓開發(fā)手冊(cè)中直接被定義為不允許了呢竞慢。首先就是一個(gè)血淋淋的教訓(xùn),直接導(dǎo)致線上服務(wù)不可用治泥,已經(jīng)可以算是事故了筹煮。

實(shí)驗(yàn)

我們也可以現(xiàn)在我們本地進(jìn)行一下小實(shí)驗(yàn):

public class ExecutorsTesting {
    private static ExecutorService executor = Executors.newFixedThreadPool(15);
    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            executor.execute(new SubThread());
        }
    }
}

class SubThread implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            //do nothing
        }
    }
}

運(yùn)行時(shí)指定JVM參數(shù):-Xmx8m -Xms8m,大概幾秒鐘之后居夹,會(huì)報(bào)出OOM錯(cuò)誤:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at com.kaikeba.mybatis.ExecutorsTesting.main(ExecutorsTesting.java:10) 
    //報(bào)錯(cuò)行數(shù)為上述代碼中的executor.execute(new SubThread());

那么為什么會(huì)報(bào)出這個(gè)錯(cuò)誤呢败潦。

源碼分析

我們先來看一下Executors中的FixedThreadPool是如何構(gòu)造的。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

可以看到對(duì)于存儲(chǔ)等待執(zhí)行的任務(wù)准脂,FixedThreadPool是通過LinkedBlockingQueue來實(shí)現(xiàn)的劫扒。而我們知道LinkedBlockingQueue是一個(gè)鏈表實(shí)現(xiàn)的阻塞隊(duì)列,而如果不設(shè)置其容量的話狸膏,將會(huì)是一個(gè)無邊界的阻塞隊(duì)列沟饥,最大長(zhǎng)度為Integer.MAX_VALUE由于Executors中并未設(shè)置容量湾戳,所以應(yīng)用可以不斷向隊(duì)列中添加任務(wù)贤旷,導(dǎo)致OOM錯(cuò)誤

上面提到的問題主要體現(xiàn)在newFixedThreadPoolnewSingleThreadExecutor兩個(gè)工廠方法上砾脑,并不是說newCachedThreadPoolnewScheduledThreadPool這兩個(gè)方法就安全了幼驶,這兩種方式創(chuàng)建的最大線程數(shù)可能是Integer.MAX_VALUE,而創(chuàng)建這么多線程韧衣,必然就有可能導(dǎo)致OOM盅藻。

如何該利用ThreadPoolExecutor來創(chuàng)建線程池呢购桑?

我們其實(shí)可以看到Executors中的newFixedThreadPool其實(shí)也是調(diào)用ThreadPoolExecutor來實(shí)現(xiàn)的。正如手冊(cè)中所說氏淑,當(dāng)我們不用Executors默認(rèn)創(chuàng)建線程池的方法勃蜘,而直接自己手動(dòng)去調(diào)用ThreadPoolExecutor,可以讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則夸政,規(guī)避資源耗盡的風(fēng)險(xiǎn)元旬。比如我們?cè)?code>Executors.newFixedThreadPool基礎(chǔ)上給LinkedBlockingQueue加一個(gè)容量,當(dāng)隊(duì)列已經(jīng)滿了守问,而仍需要添加新的請(qǐng)求會(huì)拋出相應(yīng)異常匀归,我們可以根據(jù)異常做相應(yīng)處理。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(10)); //添加容量大小
}

除了自己定義ThreadPoolExecutor外耗帕。還可以利用其它開源類庫(kù)穆端,如apache和guava等,可以有更多個(gè)性化配置仿便。

參考文章:

https://www.hollischuang.com/archives/2888

https://stackoverflow.com/questions/1094867/when-should-we-use-javas-thread-over-executor#answer-34373289

https://blog.51cto.com/zero01/2306857


本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布体啰!

文章首發(fā):https://zhuanlan.zhihu.com/lovebell

個(gè)人公眾號(hào):技術(shù)Go

您的點(diǎn)贊與支持是作者持續(xù)更新的最大動(dòng)力!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嗽仪,一起剝皮案震驚了整個(gè)濱河市荒勇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闻坚,老刑警劉巖沽翔,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窿凤,居然都是意外死亡仅偎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門雳殊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來橘沥,“玉大人,你說我怎么就攤上這事夯秃∽兀” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵仓洼,是天一觀的道長(zhǎng)箫措。 經(jīng)常有香客問我,道長(zhǎng)衬潦,這世上最難降的妖魔是什么斤蔓? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮镀岛,結(jié)果婚禮上弦牡,老公的妹妹穿的比我還像新娘友驮。我一直安慰自己,他們只是感情好驾锰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布卸留。 她就那樣靜靜地躺著,像睡著了一般椭豫。 火紅的嫁衣襯著肌膚如雪耻瑟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天赏酥,我揣著相機(jī)與錄音喳整,去河邊找鬼。 笑死裸扶,一個(gè)胖子當(dāng)著我的面吹牛框都,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呵晨,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼魏保,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了摸屠?” 一聲冷哼從身側(cè)響起谓罗,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎季二,沒想到半個(gè)月后妥衣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戒傻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜂筹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片需纳。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖艺挪,靈堂內(nèi)的尸體忽然破棺而出不翩,到底是詐尸還是另有隱情,我是刑警寧澤麻裳,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布口蝠,位于F島的核電站,受9級(jí)特大地震影響津坑,放射性物質(zhì)發(fā)生泄漏妙蔗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一疆瑰、第九天 我趴在偏房一處隱蔽的房頂上張望眉反。 院中可真熱鬧昙啄,春花似錦、人聲如沸寸五。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)梳杏。三九已至韧拒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間十性,已是汗流浹背叛溢。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烁试,地道東北人雇初。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像减响,于是被迫代替她去往敵國(guó)和親靖诗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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