(203)線程池原理

程序運行柳恐,其本質(zhì)上伐脖,是對系統(tǒng)資源(CPU热幔、內(nèi)存、磁盤讼庇、網(wǎng)絡(luò)等等)的使用绎巨。如何高效的使用這些資源是我們編程優(yōu)化演進(jìn)的一個方向。今天說的線程池是對CPU的利用的優(yōu)化手段蠕啄。

網(wǎng)上有不少介紹如何使用線程池的文章场勤,那我想說點什么呢?我希望查看線程池原理歼跟,明白池化技術(shù)的基本設(shè)計思路和媳。遇到其他相似問題可以解決。

池化技術(shù)

何為池化技術(shù)嘹承,簡單點來說窗价,就是提前保存大量的資源,以備不時之需叹卷。在資源有限的情況下撼港,該技術(shù)可以大大提升資源的利用率,提升性能等骤竹。

目前比較典型的池化技術(shù)有:
線程池帝牡、連接池、內(nèi)存池蒙揣、對象池等靶溜。

本文主要來介紹一下其中比較簡單的線程池的實現(xiàn)原理,希望讀者們可以舉一反三懒震,通過對線程池的理解罩息,學(xué)習(xí)并掌握所有的編程中池化技術(shù)的底層原理,一通百通个扰。

創(chuàng)建一個線程

在java的并發(fā)編程中瓷炮,線程是十分重要的,在Java中递宅,創(chuàng)建一個線程比較簡單:

public class App {
    public static void main(String[] args) throws Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("線程運行中");
            }
        }).start();
    }
}

我們通過創(chuàng)建一個線程對象娘香,并且實現(xiàn)Runnable接口就可以實現(xiàn)一個簡單的線程“炝洌可以利用上多核CPU烘绽。當(dāng)一個任務(wù)結(jié)束,當(dāng)前線程就結(jié)束俐填。

但很多時候安接,我們不止會執(zhí)行一個任務(wù)。如果每次都是如此的創(chuàng)建線程->執(zhí)行任務(wù)->銷毀線程英融,會造成很大的性能開銷的盏檐。

那能否一個線程創(chuàng)建后呀打,執(zhí)行完一個任務(wù)后,又去執(zhí)行另一個任務(wù)糯笙,而不是銷毀贬丛。這就是線程池。

這就是池化技術(shù)的思想给涕,通過預(yù)先創(chuàng)建好多個線程豺憔,放在池中,這樣可以在需要使用線程的時候直接獲取够庙,避免多次重復(fù)創(chuàng)建恭应、銷毀代理的開銷。

線程池的簡單使用

以下代碼耘眨,就是在java中創(chuàng)建線程池:

import java.util.concurrent.*;

public class App {
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = new ThreadPoolExecutor(1, 1,
                60L, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10));

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("abcdefg");
            }
        });

        executorService.shutdown();
    }
}

JDK提供給外部的接口也很簡單昼榛。直接調(diào)用ThreadPoolExecutor構(gòu)造一個就可以了,也可以通過Executors靜態(tài)工廠構(gòu)建剔难,但一般不建議胆屿。

可以看到,開發(fā)者想要在代碼中使用線程池還是比較簡單的偶宫,這得益于Java給我們封裝好了一系列的API非迹。很多時候,我們需要知道這些API后面干了些啥纯趋,以便于我們更好的設(shè)計與實現(xiàn)我們的代碼憎兽。

線程池構(gòu)造函數(shù)

通常,一般構(gòu)造函數(shù)會反映出這個工具或這個對象的數(shù)據(jù)存儲結(jié)構(gòu)吵冒。


構(gòu)造函數(shù)

如果把線程池比作一個公司纯命。公司會有正式員工處理正常業(yè)務(wù),如果工作量大的話痹栖,會雇傭外包人員來工作亿汞。閑時就可以釋放外包人員以減少公司管理開銷。一個公司因為成本關(guān)系结耀,雇傭的人員始終是有最大數(shù)留夜。如果這時候還有任務(wù)處理不過來匙铡,就走需求池排任務(wù)图甜。

  • acc : 獲取調(diào)用上下文
  • corePoolSize: 核心線程數(shù)量,可以類比正式員工數(shù)量鳖眼,常駐線程數(shù)量黑毅。
  • maximumPoolSize: 最大的線程數(shù)量,公司最多雇傭員工數(shù)量钦讳。常駐+臨時線程數(shù)量矿瘦。
  • workQueue:多余任務(wù)等待隊列枕面,再多的人都處理不過來了,需要等著缚去,在這個地方等潮秘。
  • keepAliveTime:非核心線程空閑時間,就是外包人員等了多久易结,如果還沒有活干枕荞,解雇了。
  • threadFactory: 創(chuàng)建線程的工廠搞动,在這個地方可以統(tǒng)一處理創(chuàng)建的線程的屬性躏精。每個公司對員工的要求不一樣,恩鹦肿,在這里設(shè)置員工的屬性矗烛。
  • handler:線程池拒絕策略,什么意思呢箩溃?就是當(dāng)任務(wù)實在是太多瞭吃,人也不夠,需求池也排滿了涣旨,還有任務(wù)咋辦虱而?默認(rèn)是不處理,拋出異常告訴任務(wù)提交者开泽,我這忙不過來了牡拇。

添加一個任務(wù)

源碼

核心模塊用紅框標(biāo)記了。

  • 第一個紅框:判斷工作的線程數(shù)有沒有超過最大核心線程數(shù)量穆律,如果沒有超過新鎮(zhèn)新的worker線程惠呼。并且推出。
  • 第二個紅框:判斷線程池是否在運行峦耘,如果在剔蹋,任務(wù)隊列是否允許插入,插入成功再次驗證線程池是否運行辅髓,如果不再運行泣崩,移除插入的任務(wù),然后拋出拒絕策略洛口。如果再運行矫付,沒有線程了,就啟用一個線程第焰。
  • 第三個紅框:如果添加非核心線程失敗买优,就直接拒絕了。

這里邏輯稍微有點復(fù)雜,畫了個流程圖僅供參考


添加任務(wù)流程.png

接下來杀赢,我們看看如何添加一個工作線程的烘跺? addWork

添加worker線程

添加work1

這里代碼有點長,沒關(guān)系脂崔,也是分塊的滤淳,總共有5個關(guān)鍵的代碼塊。

  • 第一個紅框:做是否能夠添加工作線程條件過濾砌左。
    • 判斷線程池狀態(tài)娇钱,如果線程池已經(jīng)關(guān)了,就不提交任務(wù)了绊困。
  • 第二個紅框:做自旋文搂,更新創(chuàng)建線程數(shù)量。


    添加work2
  • 第一個紅框:獲取線程池主鎖秤朗。
  • 第二個紅框:添加線程到workers中(線程池中)煤蹭。
  • 第三個紅框:啟動新建的線程。
    這樣就應(yīng)該清晰很多了取视。

有人或許會疑問 retry 是什么硝皂?這個是java中的goto語法。只能運用在break和continue后面作谭。

接下來稽物,我們看看works是什么。


works

一個hashSet折欠。
到這來就完成了一個任務(wù)的提交贝或。當(dāng)一個線程完成了首次任務(wù)的執(zhí)行,后續(xù)如何處理其他的請求的呢锐秦?

worker線程處理隊列任務(wù)

任務(wù)的執(zhí)行
  • 第一個紅框:是否是第一次執(zhí)行任務(wù)咪奖,或者從隊列中可以獲取到任務(wù)。
  • 第二個紅框:獲取到任務(wù)后酱床,執(zhí)行任務(wù)開始前操作鉤子羊赵。
  • 第三個紅框:執(zhí)行任務(wù)。
  • 第四個紅框:執(zhí)行任務(wù)后鉤子扇谣。

這兩個鉤子(beforeExecute昧捷,afterExecute)允許我們自己繼承線程池,做任務(wù)執(zhí)行前后處理罐寨。有意思靡挥。
到這里,源代碼分析到此為止衩茸。接下來做一下簡單的總結(jié)芹血。

總結(jié)

  1. 所謂線程池本質(zhì)是一個hashSet。多余的任務(wù)會放在阻塞隊列中楞慈。
  2. 只有當(dāng)阻塞隊列滿了后幔烛,才會觸發(fā)非核心線程的創(chuàng)建。所以非核心線程只是臨時過來打雜的囊蓝。直到空閑了饿悬,然后自己關(guān)閉了。
  3. 線程池提供了兩個鉤子(beforeExecute聚霜,afterExecute)給我們狡恬,我們繼承線程池,在執(zhí)行任務(wù)前后做一些事情蝎宇。
  4. 線程池原理關(guān)鍵技術(shù):鎖(lock,cas)弟劲、阻塞隊列、hashSet(資源池)
線程池原理.png

最后希望對你理解線程池有幫助姥芥。

都看到這里了兔乞,成神之路上,要不要一起凉唐?

微信公眾號rudy_tan_home

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庸追,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子台囱,更是在濱河造成了極大的恐慌淡溯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件簿训,死亡現(xiàn)場離奇詭異咱娶,居然都是意外死亡,警方通過查閱死者的電腦和手機强品,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門豺总,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人择懂,你說我怎么就攤上這事喻喳。” “怎么了困曙?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵表伦,是天一觀的道長。 經(jīng)常有香客問我慷丽,道長蹦哼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任要糊,我火速辦了婚禮纲熏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己局劲,他們只是感情好勺拣,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鱼填,像睡著了一般药有。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苹丸,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天愤惰,我揣著相機與錄音,去河邊找鬼赘理。 笑死宦言,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的商模。 我是一名探鬼主播蜡励,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阻桅!你這毒婦竟也來了凉倚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤嫂沉,失蹤者是張志新(化名)和其女友劉穎稽寒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趟章,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡杏糙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚓土。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宏侍。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蜀漆,靈堂內(nèi)的尸體忽然破棺而出谅河,到底是詐尸還是另有隱情,我是刑警寧澤确丢,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布绷耍,位于F島的核電站,受9級特大地震影響鲜侥,放射性物質(zhì)發(fā)生泄漏褂始。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一描函、第九天 我趴在偏房一處隱蔽的房頂上張望崎苗。 院中可真熱鬧狐粱,春花似錦、人聲如沸胆数。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幅慌。三九已至宋欺,卻和暖如春轰豆,著一層夾襖步出監(jiān)牢的瞬間胰伍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工酸休, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留骂租,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓斑司,卻偏偏與公主長得像渗饮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宿刮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

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

  • 第一部分 來看一下線程池的框架圖僵缺,如下: 1胡桃、Executor任務(wù)提交接口與Executors工具類 Execut...
    壓抑的內(nèi)心閱讀 4,261評論 1 24
  • 為什么使用線程池 當(dāng)我們在使用線程時,如果每次需要一個線程時都去創(chuàng)建一個線程磕潮,這樣實現(xiàn)起來很簡單翠胰,但是會有一個問題...
    閩越布衣閱讀 4,288評論 10 45
  • 本文是我自己在秋招復(fù)習(xí)時的讀書筆記,整理的知識點自脯,也是為了防止忘記之景,尊重勞動成果,轉(zhuǎn)載注明出處哦!如果你也喜歡馋嗜,那...
    波波波先森閱讀 11,257評論 4 56
  • 線程池中有一定數(shù)量的工作線程楷掉,工作線程會循環(huán)從任務(wù)隊列中獲取任務(wù),并執(zhí)行這個任務(wù)屋谭。那么怎么去停止這些工作線程呢?這...
    wo883721閱讀 1,615評論 0 14
  • 每年三四月份龟糕,是適齡兒童父母最忙碌的時候:孩子的“幼升小”被提上了最優(yōu)先的位置桐磁。最近,上海某知名小學(xué)就剛剛完成了學(xué)...
    輕語言閱讀 435評論 1 0