線程池原理及調(diào)優(yōu)

為什么要用線程池

在生產(chǎn)中,基本不會出現(xiàn)手動創(chuàng)建并啟動線程的代碼,因為這樣做有幾個弊端:

  • 頻繁創(chuàng)建線程開銷大
  • 線程的數(shù)量不可控
  • 線程數(shù)過多CPU來回切換開銷大

那么就需要一個對線程集中管理的工具,線程池應(yīng)運而生,使用線程池有如下優(yōu)勢:

  • 減少創(chuàng)建新線程的時間
  • 重復(fù)利用線程池中的線程,不需要每次創(chuàng)建
  • 利用線程池可對線程進行統(tǒng)一的監(jiān)控,分配,調(diào)優(yōu),控制最大并發(fā)數(shù)
  • 實現(xiàn)任務(wù)線程隊列緩存策略和拒絕機制
  • 隔離線程環(huán)境

JDK埋的坑

JDK為了我們方便使用,提供了幾種創(chuàng)建線程池的方法

ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService = Executors.newFixedThreadPool(n);

但是!這幾種方法不能用!

這不是我說的,阿里規(guī)范強制要求

往下翻源碼也可以看到

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

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

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

//這里隊列的長度是Integer.MAX_VALUE,容易導(dǎo)致OOM
public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}

核心類ThreadPoolExecutor

通過上面的代碼也能看到,Executors的幾個創(chuàng)建線程池的方法,底層是調(diào)用了ThreadPoolExecutor

為了避免踩坑,我們也得老老實實用ThreadPoolExecutor創(chuàng)建線程池

七大參數(shù)和底層工作原理
/**
* ThreadPoolExecutor參數(shù)最全的構(gòu)造方法
*/
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 

數(shù)了一下,一共7個參數(shù),那這7個參數(shù)分別代表什么?先看圖

  1. 主線程執(zhí)行execute或submit方法時,先判斷核心池有沒有滿了,也就是判斷正在執(zhí)行的線程數(shù)若大于或等于corePoolSize,執(zhí)行2
  2. 把任務(wù)放到阻塞隊列中排隊,若隊列滿了,執(zhí)行3
  3. 臨時創(chuàng)建新的線程,新線程的空閑時間如果超過keepAliveTime就會被銷毀,而且新創(chuàng)建的線程數(shù)+核心池的線程數(shù)不能超過maximumPoolSize,若超過,執(zhí)行4(關(guān)于這點我認為阿里的《碼出高效》那本書說錯了,大家可以自行查看源碼)
  4. 執(zhí)行相應(yīng)的拒絕策略,拒絕執(zhí)行

由此可知,這7個參數(shù)分別代表:

  • int corePoolSize 核心池大小狈蚤,阻塞隊列未滿時绞吁,最大同時執(zhí)行線程數(shù)
  • int maximumPoolSize 最大池大小麸俘,最大a同時執(zhí)行線程數(shù)
  • long keepAliveTime 最大池臨時創(chuàng)建的新線程最大空閑時間绽乔,超過則被銷毀
  • TimeUnit unit 最大空閑時間單位
  • BlockingQueue<Runnable> workQueue 阻塞隊列狞山,當(dāng)核心池已滿時码泞,新提交的線程放進阻塞隊列排隊等候
  • ThreadFactory threadFactory 線程工廠吱涉,它用來生產(chǎn)一組相同任務(wù)的結(jié)程。線程池的命名是通過給這個 factory 增加組名前綴來實現(xiàn)的
  • RejectedExecutionHandler handler 拒絕策略捺疼,當(dāng)阻塞隊列和最大池都滿了的時候疏虫,對新提交的線程執(zhí)行拒絕策略,jdk自帶四種拒絕策略
    1. DiscardPolicy:直接丟棄
    2. DiscardOldestPolicy:丟棄隊列中排隊時間最長的任務(wù)
    3. CallerRunsPolicy:將任務(wù)交給調(diào)用線程來執(zhí)行
    4. AbortPolicy:拋異常

調(diào)優(yōu)

這里說一下int maximumPoolSize參數(shù)的調(diào)優(yōu)帅涂,因為maximumPoolSize是最后一道防線了议薪,提交的線程數(shù)超過maximumPoolSize就執(zhí)行拒絕策略了,所以maximumPoolSize的大小尤其重要媳友。

CPU密集型任務(wù)

CPU密集型任務(wù)的特點是需要大量的運算斯议,CPU全速運行,較少的IO而沒有阻塞醇锚,所以對于CPU密集型任務(wù)哼御,應(yīng)該盡量減少線程切換帶來的消耗,參考配置公式:
maximumPoolSize=CPU核心數(shù)+1

IO密集型任務(wù)

IO密集型任務(wù)剛好相反焊唬,CPU占用較少恋昼,大量的阻塞,對于這種情況赶促,應(yīng)該盡量利用CPU的空閑時間液肌,最大線程數(shù)應(yīng)該配置比CPU核心數(shù)多,參考配置公式:
maximumPoolSize=CPU核心數(shù)/(1-阻塞系數(shù))
0.8<阻塞系數(shù)<0.9

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸥滨,一起剝皮案震驚了整個濱河市嗦哆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌婿滓,老刑警劉巖老速,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凸主,居然都是意外死亡橘券,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來旁舰,“玉大人锋华,你說我怎么就攤上這事△廾罚” “怎么了供置?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵谨湘,是天一觀的道長绽快。 經(jīng)常有香客問我,道長紧阔,這世上最難降的妖魔是什么坊罢? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮擅耽,結(jié)果婚禮上活孩,老公的妹妹穿的比我還像新娘。我一直安慰自己乖仇,他們只是感情好憾儒,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乃沙,像睡著了一般起趾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上警儒,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天训裆,我揣著相機與錄音,去河邊找鬼蜀铲。 笑死边琉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的记劝。 我是一名探鬼主播变姨,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼厌丑!你這毒婦竟也來了定欧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤蹄衷,失蹤者是張志新(化名)和其女友劉穎忧额,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體愧口,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡睦番,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片托嚣。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡巩检,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出示启,到底是詐尸還是另有隱情兢哭,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布夫嗓,位于F島的核電站迟螺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏舍咖。R本人自食惡果不足惜矩父,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望排霉。 院中可真熱鬧窍株,春花似錦、人聲如沸攻柠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瑰钮。三九已至,卻和暖如春飞涂,著一層夾襖步出監(jiān)牢的瞬間旦部,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工较店, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梁呈,地道東北人蝗茁。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像寻咒,于是被迫代替她去往敵國和親哮翘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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