Executors目前提供了5中不同的線程池創(chuàng)建配置:
1、newCachedThreadPool():它是一種用來處理大量短時間工作任務(wù)的線程池方灾,具有幾個鮮明特點:它會試圖緩存線程并重用建蹄,當午緩存線程可用時,就會創(chuàng)建新的工作線程裕偿;如果線程閑置的時間超過60秒洞慎,則被終止并移出緩存;長時間閑置時嘿棘,這種線程池劲腿,不會消耗什么資源。其內(nèi)部使用SynchronousQueue作為工作隊列蔫巩;
2谆棱、newFixedThreadPool(int nThreads)快压,重用指定數(shù)目(nThreads)的線程圆仔,其背后使用的時無界的工作隊列,任何時候最多有nThreads個工作線程時活動的蔫劣。如果任務(wù)數(shù)量超過了活動隊列數(shù)目坪郭,將在工作隊列中等待空閑線程出現(xiàn);如果有工作線程退出脉幢,將會有新的工作線程被創(chuàng)建歪沃,以補足指定的數(shù)目nThreads;
3嫌松、newSingleThreadExecutor()沪曙,它的特點在于工作線程數(shù)目被限制為1,操作一個無界的工作隊列萎羔,所以它保證了所有任務(wù)都是被順序執(zhí)行液走,最多會有一個任務(wù)處于活動狀態(tài),并且不允許使用者改動線程池實例贾陷,因此可以避免其改變線程數(shù)目缘眶;
4、newSingleThreadScheduleExecutor()和newScheduledThreadPool(int corePoolSize)髓废,創(chuàng)建的時個ScheduledExecutorService巷懈,可以進行定時或周期性的工作調(diào)度,區(qū)別在于單一工作線程還是多個工作線程慌洪;
5顶燕、newWorkStealingPool(int parallelism)凑保,這時一個經(jīng)常被人忽略的線程池,JAVA8才加入這個創(chuàng)建方法涌攻,其內(nèi)部會構(gòu)建ForkJoinPool愉适,利用Work-Stealing算法,并行地處理任務(wù)癣漆,不保證處理順序维咸。
各個類型的設(shè)計目的
1、Executor是一個基礎(chǔ)的接口惠爽,其初衷是將任務(wù)提交和任務(wù)執(zhí)行細節(jié)解耦癌蓖,這一點可以天匯其定義的唯一方法。
void execute(Runnable command);
2婚肆、ExecutorService則更加完善租副,不僅提供service管理,比如shutdown等方法较性,也提供了更加全面的提交任務(wù)機制用僧,如返回Future而不是 void 的 submit 方法
<T> Future<T> submit(Callable<T> task);
注意,這個例子輸入的可是Callable赞咙,它解決了Runnable無法返回結(jié)果的困擾责循。
3、Java標準類庫提供了幾種基礎(chǔ)實現(xiàn)攀操,比如?ThreadPoolExecutor院仿、ScheduledThreadPoolExecutor、ForkJoinPool速和。這些線程池設(shè)的設(shè)計特點在于其高度的可調(diào)節(jié)性和靈活性歹垫,以盡量滿足復(fù)雜多變的實際應(yīng)用場景,我會進一步分析其構(gòu)建部分的源碼颠放,剖析這種靈活性的源頭排惨。
4、Executors則從簡化使用的角度碰凶,為我們提供了各種方便的靜態(tài)工長方法暮芭。
阿里發(fā)布的 Java開發(fā)手冊中強制線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方式痒留,這樣的處理方式讓寫的同學(xué)更加明確線程池的運行規(guī)則谴麦,規(guī)避資源耗盡的風(fēng)險
下面從源碼角度,分析線程池的設(shè)計與實現(xiàn)伸头,將主要圍繞最基礎(chǔ)的ThreadPoolExecutor源碼匾效。ScheduledThreadPoolExecutor是ThreadPoolExecutor的擴展,主要是增加了調(diào)度邏輯恤磷。而ForkJoinPool則是為了ForkJoinTask定制的線程池面哼,與通常意義的線程池有所不同野宜。
在現(xiàn)實應(yīng)用中,理解應(yīng)用于線程池的交互和線程池內(nèi)部的工作過程魔策,可以參考下圖匈子。
?簡單理解一下:
1、工作隊列負責(zé)存儲用戶提交的各個任務(wù)闯袒,這個工作隊列虎敦,可以是容量為0的 SynchronousQueue(使用newCachedThreadPool),也可以是像固定大小線程池(newFixedThreadPool)那樣使用LinkedBlockingQueue政敢。
private final BlockingQueue<Runnable> workQueue;
2其徙、內(nèi)部的“線程池”,這是指保持工作線程的集合喷户,線程池需要在運行過程中管理線程創(chuàng)建唾那、銷毀。例如褪尝,對于帶緩存的線程池闹获,當任務(wù)壓力較大時,線程池會創(chuàng)建新的工作線程河哑;當業(yè)務(wù)壓力退去避诽,線程池會閑置一段時間(默認60秒)后結(jié)束線程。
private final HashSet<Worker> workers = new HashSet<Worker>();
線程池的工作線程被抽象為靜態(tài)內(nèi)部類Worker灾馒,基于AQS實現(xiàn)
3茎用、ThreadFactory提供上面所需要的創(chuàng)建線程邏輯遣总。
4睬罗、如果任務(wù)提交時被拒絕,比如線程池已處于SHUTDOWN狀態(tài)旭斥,需要為其提供處理邏輯容达,Java 標準庫提供了類似?ThreadPoolExecutor.AbortPolicy 等默認實現(xiàn),也可以按照實際需要自定義垂券。
從上面的分析花盐,就可以看出線程池的幾個基本組成部分,一起都體現(xiàn)在線程池的構(gòu)造函數(shù)中菇爪,從字面我們就可以猜測到其用意:
1算芯、corePoolSize, 稍微的核心線程數(shù),可以大致理解為長期駐留的線程數(shù)目(除非設(shè)置了allowCoreThreadTimeOut)凳宙。對于不同的線程池熙揍,這個值可能會有很大區(qū)別,比如newFixedThreadPool 會將其設(shè)置為 nThreads 氏涩,而對于newCachedThreadPool則設(shè)為0届囚。
2有梆、maximumPoolSize,顧名思義意系,就是線程不夠時能夠創(chuàng)建的最大線程數(shù)泥耀。同樣進行對比,對于newFixedThreadPool蛔添,當然就是nThreads痰催,以為其要求是固定大小,而newCachedThreadPool則是Integer.VALUE迎瞧。
3陨囊、keepAliveTime和TimeUnit,這兩個參數(shù)指定了額外的線程能夠閑置多久夹攒,顯然有些線程池不需要它蜘醋。
4、workQueue咏尝,工作隊列压语,必須是BlockingQueue。
通過配置不同的參數(shù)编检,我們就可以創(chuàng)建出行為大相徑庭的線程池胎食,這就是線程池高度靈活性的基礎(chǔ)。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
newCachedThreadPool
創(chuàng)建一個可緩存線程池允懂,如果線程池長度超過處理需要厕怜,可靈活回收空閑線程,若無可回收蕾总,則新建線程粥航。示例代碼如下:
線程池為無限大,當執(zhí)行第二個任務(wù)時第一個任務(wù)已經(jīng)完成生百,會復(fù)用執(zhí)行第一個任務(wù)的線程递雀,而不用每次新建線程。
newFixedThreadPool
創(chuàng)建一個定長線程池蚀浆,可控制線程最大并發(fā)數(shù)缀程,超出的線程會在隊列中等待。示例代碼如下:
因為線程池大小為3市俊,每個任務(wù)輸出index后sleep 2秒杨凑,所以每兩秒打印3個數(shù)字。
定長線程池的大小最好根據(jù)系統(tǒng)資源進行設(shè)置摆昧。如Runtime.getRuntime().availableProcessors()
newScheduledThreadPool
創(chuàng)建一個定長線程池撩满,支持定時及周期性任務(wù)執(zhí)行。延遲執(zhí)行示例代碼如下:
newSingleThreadExecutor
創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù)鹦牛,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行搞糕。示例代碼如下: