線程池你真的懂了嗎壕翩,什么是線程上下文切換蛉迹?用戶態(tài)和內(nèi)核態(tài)?

開足碼力放妈,碼動(dòng)人生北救,本文首發(fā)公眾號【 Craig無忌 】,關(guān)注這個(gè)一言不合就開車的的代碼界老司機(jī)

本文 GitHub上已經(jīng)收錄 https://github.com/BeKingCoding/JavaKing 芜抒, 一線大廠面試核心知識點(diǎn)珍策、我的聯(lián)系方式和技術(shù)交流群,歡迎Star和完善

前言

昨天在群里有個(gè)同學(xué)問 Java 并發(fā)編程中的線程池內(nèi)容挽绩,本篇文章就給大家介紹下這個(gè)在面試中也經(jīng)常被問到的知識點(diǎn)膛壹。

看完后相信你會(huì)線程池的原理有更清晰的認(rèn)識。本文將會(huì)從以下幾個(gè)方面來講述相關(guān)知識,相信大家耐心看了之后肯定有收獲模聋,碼字不易肩民,別忘了「在看」,「轉(zhuǎn)發(fā)」哦链方。

  • 為什么要使用線程池
  • 線程池的工作原理
  • 線程池的7大核心參數(shù)
  • 如何正確地使用線程池

正文

**

01 為什么要使用線程池

**
引入一個(gè)技術(shù)之前持痰,首先應(yīng)該解答的問題是,這個(gè)技術(shù)解決什么問題祟蚀。

在 Java 語言中工窍,創(chuàng)建一個(gè)線程看上去非常簡單。實(shí)現(xiàn)Runnable接口前酿,然后像創(chuàng)建一個(gè)對象一樣患雏,直接 new Thread 就可以了。

但實(shí)際上線程的創(chuàng)建和銷毀遠(yuǎn)不是創(chuàng)建一個(gè)對象那么簡單罢维。線程的創(chuàng)建需要調(diào)用操作系統(tǒng)內(nèi)核的 API淹仑,然后操作系統(tǒng)為其分配一系列資源,所以整個(gè)成本很高肺孵,導(dǎo)致線程是一個(gè)重量級的對象匀借,應(yīng)該避免頻繁創(chuàng)建和銷毀。

再來說說線程的上下文切換平窘。

一個(gè) CPU 在一個(gè)時(shí)刻只能運(yùn)行一個(gè)線程吓肋,當(dāng)其運(yùn)行一個(gè)線程時(shí),由于時(shí)間片耗盡或出現(xiàn)阻塞等情況瑰艘,CPU 會(huì)轉(zhuǎn)去執(zhí)行另外一個(gè)線程是鬼,這個(gè)叫做線程上下文切換。

并且當(dāng)前線程的任務(wù)可能并沒有執(zhí)行完畢磅叛,所以在進(jìn)行切換時(shí)需要保存線程的運(yùn)行狀態(tài)屑咳,以便下次重新切換回來時(shí),能夠繼續(xù)切換之前的狀態(tài)運(yùn)行弊琴,這個(gè)過程就要涉及到用戶態(tài)和內(nèi)核態(tài)的切換兆龙。

在這里插入圖片描述

什么是用戶態(tài)和內(nèi)核態(tài)?

當(dāng)在執(zhí)行用戶自己的代碼時(shí)敲董,則稱其處于用戶運(yùn)行態(tài)(用戶態(tài))紫皇,此時(shí)處理器特權(quán)級最低,是普通的用戶進(jìn)程運(yùn)行的特權(quán)級腋寨,大部分用戶直接面對的程序都是運(yùn)行在用戶態(tài)聪铺。

當(dāng)因?yàn)橄到y(tǒng)調(diào)用陷入內(nèi)核代碼中執(zhí)行時(shí),處于內(nèi)核運(yùn)行態(tài)(內(nèi)核態(tài))萄窜,此時(shí)處理器處于特權(quán)級最高铃剔。如果要執(zhí)行文件操作撒桨、網(wǎng)絡(luò)數(shù)據(jù)發(fā)送等操作必須通過 write、send 等系統(tǒng)調(diào)用键兜,這些系統(tǒng)調(diào)用會(huì)調(diào)用內(nèi)核的代碼凤类。會(huì)從用戶態(tài)切換到內(nèi)核態(tài)的內(nèi)核地址空間去執(zhí)行內(nèi)核代碼來完成相應(yīng)的操作,在執(zhí)行完后又會(huì)切換回用戶態(tài)普气。

如果并發(fā)的線程數(shù)量很多谜疤,并且每個(gè)線程都是執(zhí)行一個(gè)時(shí)間很短的任務(wù)就結(jié)束了,這樣頻繁創(chuàng)建線程就會(huì)大大降低系統(tǒng)的效率现诀,因?yàn)轭l繁創(chuàng)建線程和銷毀線程需要時(shí)間夷磕。

為了避免資源過度消耗,所以最好的一種辦法就是對線程進(jìn)行復(fù)用仔沿,它執(zhí)行完一個(gè)任務(wù)坐桩,并不需要被銷毀,而是讓它繼續(xù)執(zhí)行其他任務(wù)于未。

線程池是一種線程的使用模式撕攒,帶來了一系列好處:

(1)避免了線程的重復(fù)創(chuàng)建與開銷帶來的資源消耗代價(jià)陡鹃。

(2)提升了任務(wù)響應(yīng)速度烘浦,任務(wù)到達(dá)時(shí),直接選一個(gè)線程執(zhí)行而無需等待線程的創(chuàng)建萍鲸。

(3)提高線程的可管理性闷叉,線程的統(tǒng)一分配和管理,也方便統(tǒng)一的監(jiān)控和調(diào)優(yōu)脊阴。

這就是線程池最核心的設(shè)計(jì)思路握侧,復(fù)用線程,平攤線程的創(chuàng)建與銷毀的開銷代價(jià)嘿期。

02 線程池的工作原理

在這里插入圖片描述

線程池的工作原理可以簡化理解為以下幾個(gè)步驟:

(1)在線程池的內(nèi)部品擎,會(huì)維護(hù)了一個(gè)阻塞隊(duì)列 workQueue 和一組工作線程,工作線程的個(gè)數(shù)可以在初始化線程池的時(shí)候來指定备徐。

(2)用戶可以將需要完成的任務(wù)提交給線程池萄传,任務(wù)會(huì)被加入到 workQueue中。

(3)線程池內(nèi)部維護(hù)的工作線程會(huì)按照次序蜜猾,依次消費(fèi) workQueue 中的任務(wù)并進(jìn)行執(zhí)行秀菱,在執(zhí)行結(jié)束后并不會(huì)銷毀。

03 線程池的7大核心參數(shù)

我們可以通過 ThreadPoolExecutor 來創(chuàng)建線程池蹭睡,創(chuàng)建的時(shí)候需要指定7大核心參數(shù)衍菱,每一個(gè)參數(shù)都代表線程池的特定工作行為,非常重要肩豁。

corePoolSize(核心線程數(shù))

將把線程池類比為一個(gè)施工隊(duì)脊串,而線程就是施工隊(duì)的工人辫呻。有些時(shí)候比較閑,項(xiàng)目比較少琼锋,但是施工隊(duì)也不能把工人都遣散印屁,需要留下一些核心骨干來以備不時(shí)之需,所以至少要留 corePoolSize 個(gè)人堅(jiān)守陣地斩例。

corePoolSize 表示線程池保有的核心線程數(shù)雄人,核心線程會(huì)一直存活,即使這些線程處于空閑狀態(tài)沒有任務(wù)執(zhí)行念赶,他們也不會(huì)被銷毀础钠。

maximumPoolSize(最大線程數(shù))

當(dāng)項(xiàng)目比較多的時(shí)候,施工隊(duì)就需要增加工人叉谜,但是也不能無限制地加旗吁。最多就加到 maximumPoolSize 個(gè)人,當(dāng)閑下來的時(shí)候停局,施工隊(duì)就要遣散工人很钓,但是至少保留corePoolSize 個(gè)人。

keepAliveTime&unit(存活時(shí)間&單位)

上面提到施工隊(duì)根據(jù)忙閑董栽,項(xiàng)目多少來增減工人码倦,那在編程世界里,如何定義忙和閑呢锭碳?

很簡單袁稽,當(dāng)線程池內(nèi)部的線程數(shù)已經(jīng)大于 corePoolSize 的時(shí)候,一個(gè)線程如果在一段時(shí)間內(nèi)擒抛,都沒有執(zhí)行任務(wù)推汽,說明很閑。

keepAliveTime 和 unit 就是用來定義這個(gè)“一段時(shí)間”的參數(shù)歧沪。也就是說歹撒,如果一個(gè)線程空閑了keepAliveTime & unit 這么久,那么這個(gè)空閑的線程就要被回收了诊胞。

workQueue(工作隊(duì)列)

新任務(wù)被提交后暖夭,會(huì)先進(jìn)入到此工作隊(duì)列中,任務(wù)調(diào)度時(shí)再從隊(duì)列中取出任務(wù)厢钧。

threadFactory(線程工廠)

創(chuàng)建一個(gè)新線程時(shí)使用的工廠鳞尔,通過這個(gè)工廠可以自定義如何創(chuàng)建線程,例如可以給線程指定一個(gè)有意義的名字早直。

handler(拒絕策略)

如果線程池中所有的線程都在忙碌寥假,并且工作隊(duì)列也滿了(前提是工作隊(duì)列是有界隊(duì)列),那么此時(shí)提交任務(wù)霞扬,線程池就會(huì)拒絕接收糕韧。

至于拒絕的策略枫振,可以通過 handler 這個(gè)參數(shù)來指定:

  • CallerRunsPolicy:提交任務(wù)的線程自己去執(zhí)行該任務(wù)。

  • AbortPolicy:默認(rèn)的拒絕策略萤彩,直接丟棄任務(wù)粪滤,拋出RejectedExecutionException。

  • DiscardPolicy:直接丟棄任務(wù)雀扶,沒有任何異常拋出杖小。

  • DiscardOldestPolicy:丟棄最老的任務(wù),其實(shí)就是把最早進(jìn)入工作隊(duì)列的任務(wù)丟棄愚墓,然后把新任務(wù)加入到工作隊(duì)列予权。

04 如何正確地使用線程池

默認(rèn)的拒絕策略要慎重使用。如果線程池處理的任務(wù)非常重要浪册,建議自定義自己的拒絕策略扫腺;并且在實(shí)際工作中,自定義的拒絕策略往往和降級策略配合使用村象。

使用線程池笆环,需要注意異常處理的問題。任務(wù)在執(zhí)行的過程中出現(xiàn)運(yùn)行時(shí)異常厚者,會(huì)導(dǎo)致執(zhí)行任務(wù)的線程終止躁劣,最穩(wěn)妥和簡單的方案還是捕獲所有異常并按需處理。

需要注意的一點(diǎn)籍救,在《阿里巴巴Java開發(fā)手冊》也著重強(qiáng)調(diào)习绢,盡可能不要使用Executors 工具類來直接創(chuàng)建線程池,通過 ThreadPoolExecutor 的方式蝙昙,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)梧却。

Executors 返回的線程池對象的弊端如下:
(1)FixedThreadPool 和 SingleThreadPool 允許的請求隊(duì)列長度為 Integer.MAX_VALUE奇颠,可能會(huì)堆積大量的請求,從而導(dǎo)致 OOM放航。

(2)CachedThreadPool 和 ScheduledThreadPool 允許的創(chuàng)建線程數(shù)量為 Integer.MAX_VALUE烈拒,可能會(huì)創(chuàng)建大量的線程,從而導(dǎo)致 OOM广鳍。

文末福利

最近各大互聯(lián)網(wǎng)公司的秋招都陸陸續(xù)續(xù)開始了荆几,還在找工作的小伙伴可以后臺回復(fù)關(guān)鍵字進(jìn)入對應(yīng)的秋招/內(nèi)推/面試群,我給大家整理了各大公司的內(nèi)推通道赊时、簡歷模板還有歷年的筆試題吨铸,大家要好好準(zhǔn)備哦。還可以幫助大家免費(fèi)修改簡歷祖秒、模擬面試哦~

關(guān)注公眾號「Craig無忌」

創(chuàng)作不易诞吱,各位的支持和認(rèn)可舟奠,就是我創(chuàng)作的最大動(dòng)力,我們下篇文章見房维!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沼瘫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子咙俩,更是在濱河造成了極大的恐慌耿戚,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阿趁,死亡現(xiàn)場離奇詭異溅话,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)歌焦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門飞几,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人独撇,你說我怎么就攤上這事屑墨。” “怎么了纷铣?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵卵史,是天一觀的道長。 經(jīng)常有香客問我搜立,道長以躯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任啄踊,我火速辦了婚禮忧设,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颠通。我一直安慰自己址晕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布顿锰。 她就那樣靜靜地躺著谨垃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪硼控。 梳的紋絲不亂的頭發(fā)上刘陶,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機(jī)與錄音牢撼,去河邊找鬼匙隔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛浪默,可吹牛的內(nèi)容都是我干的牡直。 我是一名探鬼主播缀匕,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碰逸!你這毒婦竟也來了乡小?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤饵史,失蹤者是張志新(化名)和其女友劉穎满钟,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胳喷,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湃番,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吭露。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吠撮。...
    茶點(diǎn)故事閱讀 38,789評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖讲竿,靈堂內(nèi)的尸體忽然破棺而出泥兰,到底是詐尸還是另有隱情,我是刑警寧澤题禀,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布鞋诗,位于F島的核電站,受9級特大地震影響迈嘹,放射性物質(zhì)發(fā)生泄漏削彬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一秀仲、第九天 我趴在偏房一處隱蔽的房頂上張望融痛。 院中可真熱鬧,春花似錦啄育、人聲如沸酌心。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至墩崩,卻和暖如春氓英,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹦筹。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工铝阐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人铐拐。 一個(gè)月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓徘键,卻偏偏與公主長得像练对,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子吹害,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評論 2 351

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