支持生產(chǎn)阻塞的線程池

我們使用線程池的時(shí)候逸寓,經(jīng)常使用默認(rèn)的丟棄策略泞遗,那么我們也可以自定義策略,那么下面的文章可以看下席覆。

在各種并發(fā)編程模型中史辙,生產(chǎn)者-消費(fèi)者模式大概是最常用的了。在實(shí)際工作中佩伤,對于生產(chǎn)消費(fèi)的速度聊倔,通常需要做一下權(quán)衡。通常來說生巡,生產(chǎn)任務(wù)的速度要大于消費(fèi)的速度耙蔑。一個(gè)細(xì)節(jié)問題是,隊(duì)列長度孤荣,以及如何匹配生產(chǎn)和消費(fèi)的速度甸陌。
一個(gè)典型的生產(chǎn)者-消費(fèi)者模型如下:

Paste_Image.png

在并發(fā)環(huán)境下利用J.U.C提供的Queue實(shí)現(xiàn)可以很方便地保證生產(chǎn)和消費(fèi)過程中的線程安全须揣。這里需要注意的是,Queue必須設(shè)置初始容量钱豁,防止生產(chǎn)者生產(chǎn)過快導(dǎo)致隊(duì)列長度暴漲耻卡,最終觸發(fā)OutOfMemory。

對于一般的生產(chǎn)快于消費(fèi)的情況牲尺。當(dāng)隊(duì)列已滿時(shí)卵酪,我們并不希望有任何任務(wù)被忽略或得不到執(zhí)行,此時(shí)生產(chǎn)者可以等待片刻再提交任務(wù)谤碳,更好的做法是溃卡,把生產(chǎn)者阻塞在提交任務(wù)的方法上,待隊(duì)列未滿時(shí)繼續(xù)提交任務(wù)蜒简,這樣就沒有浪費(fèi)的空轉(zhuǎn)時(shí)間了瘸羡。阻塞這一點(diǎn)也很容易,BlockingQueue就是為此打造的搓茬,ArrayBlockingQueue和LinkedBlockingQueue在構(gòu)造時(shí)都可以提供容量做限制犹赖,其中LinkedBlockingQueue是在實(shí)際操作隊(duì)列時(shí)在每次拿到鎖以后判斷容量。

更進(jìn)一步垮兑,當(dāng)隊(duì)列為空時(shí)冷尉,消費(fèi)者拿不到任務(wù),可以等一會兒再拿系枪,更好的做法是雀哨,用BlockingQueue的take方法,阻塞等待私爷,當(dāng)有任務(wù)時(shí)便可以立即獲得執(zhí)行雾棺,建議調(diào)用take的帶超時(shí)參數(shù)的重載方法,超時(shí)后線程退出衬浑。這樣當(dāng)生產(chǎn)者事實(shí)上已經(jīng)停止生產(chǎn)時(shí)捌浩,不至于讓消費(fèi)者無限等待。

于是一個(gè)高效的支持阻塞的生產(chǎn)消費(fèi)模型就實(shí)現(xiàn)了工秩。
等一下尸饺,既然J.U.C已經(jīng)幫我們實(shí)現(xiàn)了線程池,為什么還要采用這一套東西助币?直接用ExecutorService不是更方便浪听?

我們來看一下ThreadPoolExecutor的基本結(jié)構(gòu):

Paste_Image.png

可以看到,在ThreadPoolExecutor中眉菱,BlockingQueue和Consumer部分已經(jīng)幫我們實(shí)現(xiàn)好了迹栓,并且直接采用線程池的實(shí)現(xiàn)還有很多優(yōu)勢,例如線程數(shù)的動(dòng)態(tài)調(diào)整等俭缓。

但問題在于克伊,即便你在構(gòu)造ThreadPoolExecutor時(shí)手動(dòng)指定了一個(gè)BlockingQueue作為隊(duì)列實(shí)現(xiàn)酥郭,事實(shí)上當(dāng)隊(duì)列滿時(shí),execute方法并不會阻塞愿吹,原因在于ThreadPoolExecutor調(diào)用的是BlockingQueue非阻塞的offer方法:

Paste_Image.png

這時(shí)候就需要做一些事情來達(dá)成一個(gè)結(jié)果:當(dāng)生產(chǎn)者提交任務(wù)不从,而隊(duì)列已滿時(shí),能夠讓生產(chǎn)者阻塞住洗搂,等待任務(wù)被消費(fèi)消返。

關(guān)鍵在于载弄,在并發(fā)環(huán)境下耘拇,隊(duì)列滿不能由生產(chǎn)者去判斷,不能調(diào)用ThreadPoolExecutor.getQueue().size()來判斷隊(duì)列是否滿宇攻。
線程池的實(shí)現(xiàn)中惫叛,當(dāng)隊(duì)列滿時(shí)會調(diào)用構(gòu)造時(shí)傳入的RejectedExecutionHandler去拒絕任務(wù)的處理。默認(rèn)的實(shí)現(xiàn)是AbortPolicy逞刷,直接拋出一個(gè)RejectedExecutionException嘉涌。

幾種拒絕策略在這里就不贅述了,這里和我們的需求比較接近的是CallerRunsPolicy夸浅,這種策略會在隊(duì)列滿時(shí)仑最,讓提交任務(wù)的線程去執(zhí)行任務(wù),相當(dāng)于讓生產(chǎn)者臨時(shí)去干了消費(fèi)者干的活兒帆喇,這樣生產(chǎn)者雖然沒有被阻塞警医,但提交任務(wù)也會被暫停。

Paste_Image.png

但這種策略也有隱患坯钦,當(dāng)生產(chǎn)者較少時(shí)预皇,生產(chǎn)者消費(fèi)任務(wù)的時(shí)間里,消費(fèi)者可能已經(jīng)把任務(wù)都消費(fèi)完了婉刀,隊(duì)列處于空狀態(tài)吟温,當(dāng)生產(chǎn)者執(zhí)行完任務(wù)后才能再繼續(xù)生產(chǎn)任務(wù),這個(gè)過程中可能導(dǎo)致消費(fèi)者線程的饑餓突颊。

參考類似的思路鲁豪,最簡單的做法,我們可以直接定義一個(gè)RejectedExecutionHandler律秃,當(dāng)隊(duì)列滿時(shí)改為調(diào)用BlockingQueue.put來實(shí)現(xiàn)生產(chǎn)者的阻塞:

Paste_Image.png

這樣爬橡,我們就無需再關(guān)心Queue和Consumer的邏輯,只要把精力集中在生產(chǎn)者和消費(fèi)者線程的實(shí)現(xiàn)邏輯上友绝,只管往線程池提交任務(wù)就行了堤尾。

相比最初的設(shè)計(jì),這種方式的代碼量能減少不少迁客,而且能避免并發(fā)環(huán)境的很多問題郭宝。當(dāng)然辞槐,你也可以采用另外的手段,例如在提交時(shí)采用信號量做入口限制等粘室,但是如果僅僅是要讓生產(chǎn)者阻塞榄檬,那就顯得復(fù)雜了。

最后編輯于
?著作權(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)容