一、簡介
黔江網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián),黔江網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為黔江超過千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站制作要多少錢,請找那個(gè)售后服務(wù)好的黔江做網(wǎng)站的公司定做!
jdk1.5 版本新增了JUC并發(fā)編程包,大大的簡化了傳統(tǒng)的多線程開發(fā)。
Java線程池,是典型的池化思想的產(chǎn)物,類似的還有數(shù)據(jù)庫的連接池、redis的連接池等。池化思想,就是在初始的時(shí)候去申請資源,創(chuàng)建一批可使用的連接,這樣在使用的時(shí)候,就不必再進(jìn)行創(chuàng)建連接信息的開銷了。舉個(gè)生活中鮮明的例子,在去著名洋快餐某基或者某勞的時(shí)候,配餐人員是字節(jié)從一個(gè)中間的保溫箱里面直接取,然后打包就好了。不用再臨時(shí)的來了一個(gè)單子,又要去拿原材料,又要去進(jìn)行加工。效率明顯的就是提高了很多。
既然是池子,那么必然的會存在最大值、初始值以及存活值等屬性。在達(dá)到某些特定條件的時(shí)候,再來請求的話,池子是如何進(jìn)行請求處理的呢?這里就引出了池的拒絕策略。一般的數(shù)據(jù)庫連接池在達(dá)到最大連接數(shù)的時(shí)候會默認(rèn)的等待特定的設(shè)置的時(shí)間或者直接就拋出異常。而本文中要闡述的線程池卻并非如此的策略,下面開始展開講解下。
二、線程池的拒絕策略
線程池中,有三個(gè)重要的參數(shù),決定影響了拒絕策略:corePoolSize - 核心線程數(shù),也即最小的線程數(shù)。workQueue - 阻塞隊(duì)列 。maximumPoolSize - 最大線程數(shù)
當(dāng)提交任務(wù)數(shù)大于 corePoolSize 的時(shí)候,會優(yōu)先將任務(wù)放到 workQueue 阻塞隊(duì)列中。當(dāng)阻塞隊(duì)列飽和后,會擴(kuò)充線程池中線程數(shù),直到達(dá)到 maximumPoolSize 最大線程數(shù)配置。此時(shí),再多余的任務(wù),則會觸發(fā)線程池的拒絕策略了。
總結(jié)起來,也就是一句話,當(dāng)提交的任務(wù)數(shù)大于(workQueue.size() + maximumPoolSize ),就會觸發(fā)線程池的拒絕策略。
三、拒絕策略定義
拒絕策略提供頂級接口 RejectedExecutionHandler ,其中方法 rejectedExecution 即定制具體的拒絕策略的執(zhí)行邏輯。
jdk默認(rèn)提供了四種拒絕策略:CallerRunsPolicy - 當(dāng)觸發(fā)拒絕策略,只要線程池沒有關(guān)閉的話,則使用調(diào)用線程直接運(yùn)行任務(wù)。
一般并發(fā)比較小,性能要求不高,不允許失敗。但是,由于調(diào)用者自己運(yùn)行任務(wù),如果任務(wù)提交速度過快,可能導(dǎo)致程序阻塞,性能效率上必然的損失較大
AbortPolicy - 丟棄任務(wù),并拋出拒絕執(zhí)行 RejectedExecutionException 異常信息。線程池默認(rèn)的拒絕策略。必須處理好拋出的異常,否則會打斷當(dāng)前的執(zhí)行流程,影響后續(xù)的任務(wù)執(zhí)行。
DiscardPolicy - 直接丟棄,其他啥都沒有
DiscardOldestPolicy - 當(dāng)觸發(fā)拒絕策略,只要線程池沒有關(guān)閉的話,丟棄阻塞隊(duì)列 workQueue 中最老的一個(gè)任務(wù),并將新任務(wù)加入
四、測試代碼
1、AbortPolicy
package com.cfang; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; @Slf4j public class T2 { public static void main(String[] args) throws Exception{ int corePoolSize = 5; int maximumPoolSize = 10; long keepAliveTime = 5; BlockingQueueworkQueue = new LinkedBlockingQueue (10); RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, handler); for(int i=0; i<100; i++) { try { executor.execute(new Thread(() -> log.info(Thread.currentThread().getName() + " is running"))); } catch (Exception e) { log.error(e.getMessage()); } } executor.shutdown(); } }
如果 executor.execute()提交任務(wù),由于會拋出 RuntimeException,沒有try.catch處理異常信息的話,會中斷調(diào)用者的處理流程,后續(xù)任務(wù)得不到執(zhí)行(跑不完100個(gè))??勺孕袦y試下。
2、CallerRunsPolicy
主體代碼同上,更換拒絕策略:
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
運(yùn)行后,在控制臺console中能夠看到的是,會有一部分的數(shù)據(jù)打印,顯示的是 “main is running”,也即體現(xiàn)調(diào)用線程處理。
3、DiscardPolicy
更換拒絕策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
直接丟棄任務(wù),實(shí)際運(yùn)行中,打印出的信息不會有100條。
4、DiscardOldestPolicy
同樣的,更換拒絕策略:
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
實(shí)際運(yùn)行,打印出的信息也會少于100條。
五、總結(jié)
四種拒絕策略是相互獨(dú)立無關(guān)的,選擇何種策略去執(zhí)行,還得結(jié)合具體的業(yè)務(wù)場景。實(shí)際工作中,一般直接使用 ExecutorService 的時(shí)候,都是使用的默認(rèn)的 defaultHandler ,也即 AbortPolicy 策略。