Java 在 ThreadPoolExecutor 類中為我們提供了 4 種默認(rèn)的拒絕策略來(lái)應(yīng)對(duì)不同的場(chǎng)景,都實(shí)現(xiàn)了 RejectedExecutionHandler 接口捧搞。
拒絕策略
- ThreadPoolExecutor.AbortPolicy()
- 這種拒絕策略在拒絕任務(wù)時(shí),會(huì)直接拋出一個(gè)類型為 RejectedExecutionException 的RuntimeException,讓你感知到任務(wù)被拒絕了,于是你便可以根據(jù)業(yè)務(wù)邏輯選擇重試或者放棄提交等策略爷耀。
- ThreadPoolExecutor.DiscardPolicy()
- 這種拒絕策略正如它的名字所描述的一樣,當(dāng)新任務(wù)被提交后直接被丟棄掉淑际,也不會(huì)給你任何的通知畏纲,相對(duì)而言存在一定的風(fēng)險(xiǎn)扇住,因?yàn)槲覀兲峤坏臅r(shí)候根本不知道這個(gè)任務(wù)會(huì)被丟棄春缕,可能造成數(shù)據(jù)丟失。
- ThreadPoolExecutor.DiscardOldestPolicy()
- 如果線程池沒(méi)被關(guān)閉且沒(méi)有能力執(zhí)行艘蹋,則會(huì)丟棄任務(wù)隊(duì)列中的頭結(jié)點(diǎn)锄贼,通常是存活時(shí)間最長(zhǎng)的任務(wù),這種策略與第二種不同之處在于它丟棄的不是最新提交的女阀,而是隊(duì)列中存活時(shí)間最長(zhǎng)的宅荤,這樣就可以騰出空間給新提交的任務(wù),但同理它也存在一定的數(shù)據(jù)丟失風(fēng)險(xiǎn)浸策。
- ThreadPoolExecutor.CallerRunsPolicy()
- 相對(duì)而言它就比較完善了冯键,當(dāng)有新任務(wù)提交后,如果線程池沒(méi)被關(guān)閉且沒(méi)有能力執(zhí)行庸汗,則把這個(gè)任務(wù)交于提交任務(wù)的線程執(zhí)行惫确,也就是誰(shuí)提交任務(wù),誰(shuí)就負(fù)責(zé)執(zhí)行任務(wù)蚯舱。這樣做主要有兩點(diǎn)好處改化。
- 第一點(diǎn)新提交的任務(wù)不會(huì)被丟棄,這樣也就不會(huì)造成業(yè)務(wù)損失枉昏。
- 第二點(diǎn)好處是陈肛,由于誰(shuí)提交任務(wù)誰(shuí)就要負(fù)責(zé)執(zhí)行任務(wù),這樣提交任務(wù)的線程就得負(fù)責(zé)執(zhí)行任務(wù)兄裂,而執(zhí)行任務(wù)又是比較耗時(shí)的句旱,在這段期間阳藻,提交任務(wù)的線程被占用,也就不會(huì)再提交新的任務(wù)谈撒,減緩了任務(wù)提交的速度稚配,相當(dāng)于是一個(gè)負(fù)反饋。在此期間港华,線程池中的線程也可以充分利用這段時(shí)間來(lái)執(zhí)行掉一部分任務(wù)道川,騰出一定的空間,相當(dāng)于是給了線程池一定的緩沖期立宜。
- 相對(duì)而言它就比較完善了冯键,當(dāng)有新任務(wù)提交后,如果線程池沒(méi)被關(guān)閉且沒(méi)有能力執(zhí)行庸汗,則把這個(gè)任務(wù)交于提交任務(wù)的線程執(zhí)行惫确,也就是誰(shuí)提交任務(wù),誰(shuí)就負(fù)責(zé)執(zhí)行任務(wù)蚯舱。這樣做主要有兩點(diǎn)好處改化。
拒絕時(shí)機(jī)
首先冒萄,新建線程池時(shí)可以指定它的任務(wù)拒絕策略,例如:
newThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(),
new ThreadPoolExecutor.DiscardOldestPolicy());
拒絕任務(wù)的時(shí)機(jī)是什么呢橙数? 線程池會(huì)在以下兩種情況下會(huì)拒絕新提交的任務(wù)尊流。
- 第一種情況是當(dāng)我們調(diào)用 shutdown 等方法關(guān)閉線程池后,即便此時(shí)可能線程池內(nèi)部依然有沒(méi)執(zhí)行完的任務(wù)正在執(zhí)行灯帮,但是由于線程池已經(jīng)關(guān)閉崖技,此時(shí)如果再向線程池內(nèi)提交任務(wù),就會(huì)遭到拒絕钟哥。
第二種情況是線程池沒(méi)有能力繼續(xù)處理新提交的任務(wù)迎献,也就是工作已經(jīng)非常飽和的時(shí)候。 - 第二種情況腻贰,也就是由于工作飽和導(dǎo)致的拒絕吁恍。比如新建一個(gè)線程池,使用容量上限為 10 的 ArrayBlockingQueue 作為任務(wù)隊(duì)列播演,并且指定線程池的核心線程數(shù)為 5冀瓦,最大線程數(shù)為 10,假設(shè)此時(shí)有 20 個(gè)耗時(shí)任務(wù)被提交写烤,在這種情況下翼闽,線程池會(huì)首先創(chuàng)建核心數(shù)量的線程,也就是5個(gè)線程來(lái)執(zhí)行任務(wù)洲炊,然后往隊(duì)列里去放任務(wù)感局,隊(duì)列的 10 個(gè)容量被放滿了之后,會(huì)繼續(xù)創(chuàng)建新線程选浑,直到達(dá)到最大線程數(shù) 10蓝厌。此時(shí)線程池中一共有 20 個(gè)任務(wù),其中 10 個(gè)任務(wù)正在被 10 個(gè)線程執(zhí)行古徒,還有 10 個(gè)任務(wù)在任務(wù)隊(duì)列中等待拓提,而且由于線程池的最大線程數(shù)量就是 10,所以已經(jīng)不能再增加更多的線程來(lái)幫忙處理任務(wù)了隧膘,這就意味著此時(shí)線程池工作飽和代态,這個(gè)時(shí)候再提交新任務(wù)時(shí)就會(huì)被拒絕寺惫。