亦余心之所善兮,雖九死其猶未悔措译。———屈原《離騷》
這句話的意思是“這些都是我內(nèi)心之所珍愛饰序,就是讓我九死(或多死)還是不后悔领虹。”這兩句表現(xiàn)了詩人對美好理想執(zhí)著追求的精神求豫。
PS: 如果覺得本文有用的話塌衰,請幫忙點贊,留言評論支持一下哦蝠嘉,您的支持是我最大的動力最疆!謝謝啦~
這篇文章介紹 Executor 框架,我用 Xmind 軟件畫了這篇文章內(nèi)容的思維導(dǎo)圖蚤告。這是我第一次用思維導(dǎo)圖軟件努酸,發(fā)現(xiàn)真的很好用,將整個文章提取然后可視化杜恰,復(fù)習(xí)起來也一目了然获诈。話不所說,誰用誰知道箫章。下面放上這篇文章的思維導(dǎo)圖:
Executor 簡介
從代碼上看烙荷,Executor 是一個簡單的接口镜会,但它卻是整個異步任務(wù)執(zhí)行框架的基礎(chǔ)檬寂,這個框架能支持多種不同類型的任務(wù)執(zhí)行策略。他提供了一種標準的方法將任務(wù)的提交過程和執(zhí)行過程解耦開來戳表,任務(wù)用 Runnable 來表示桶至。Executor 基于生產(chǎn)者-消費者模式昼伴,提交任務(wù)的線程相當于生產(chǎn)者,執(zhí)行任務(wù)的線程相當于消費者镣屹。同時圃郊,Executor 的實現(xiàn)還提供了對任務(wù)執(zhí)行的生命周期管理的支持。
Executor 引入的原因
大多數(shù)并發(fā)應(yīng)用程序都是圍繞[任務(wù)執(zhí)行]來構(gòu)造女蜈,應(yīng)用程序的工作可以被分解到多個任務(wù)中持舆,關(guān)鍵是如何安排好這些任務(wù)的執(zhí)行。換句話說伪窖,就是有哪些執(zhí)行策略可以供我們選擇逸寓,來保證程序的正常運行。執(zhí)行策略有下面三種方法覆山,我們來看看竹伸。
串行執(zhí)行
最簡單最直接最粗暴的方法就是單線程的串行執(zhí)行,每次只能執(zhí)行一個任務(wù)簇宽,其他任務(wù)必須等待當前任務(wù)執(zhí)行完成勋篓,才能進行。這在理論上可以的魏割,但是它的缺陷也非常明顯譬嚣。假如當前任務(wù)是耗時操作或者是阻塞的,那么其他任務(wù)需要進行長時間的等待钞它,這讓客戶端感覺到程序響應(yīng)非常慢孤荣。特別是在服務(wù)器應(yīng)用程序中,串行處理機制通常無法提供高吞吐率或快速響應(yīng)性须揣。它的缺陷總結(jié)如下:
- 吞吐率低
- 響應(yīng)慢
- 資源利用率低
無限制創(chuàng)建線程
這時盐股,我們可能會想到的另一個方法,就是為每個任務(wù)都單獨分配一個線程來執(zhí)行耻卡,從而提高程序的響應(yīng)性疯汁。因為這樣可以做到任務(wù)并行處理,能同時處理多個請求卵酪,但是要保證任務(wù)處理代碼是線程安全的幌蚊。
然而,這種方法也存在著一定的缺陷溃卡,極端情況下溢豆,它會創(chuàng)建大量的線程,超過了一定的數(shù)量瘸羡,它繼續(xù)創(chuàng)建線程會降低程序的執(zhí)行速度漩仙,甚至導(dǎo)致程序崩潰。所以,當需要創(chuàng)建大量線程時队他,它的缺點有:
- 線程生命周期的開銷非常高卷仑,會消耗大量的計算資源。
- 資源消耗問題麸折,大量空閑線程會占用很多內(nèi)存锡凝。
- 穩(wěn)定性問題,超過了系統(tǒng)的限制數(shù)量垢啼,可能會導(dǎo)致OOM窜锯。
線程池的優(yōu)勢
上面提到的兩種方式,都有著各自明顯的缺點芭析。串行執(zhí)行的問題在于糟糕的響應(yīng)性和吞吐率衬浑;為每個任務(wù)都創(chuàng)建線程的問題在于資源管理的復(fù)雜性。這兩種方法看起來都是極端的情況放刨,要么只創(chuàng)建一個工秩,要么無線創(chuàng)建。
那么进统,我們是否可以通過限制線程的數(shù)量助币,找到一種介于兩者之間平衡的方法來解決這些問題呢?答案是肯定的螟碎。各種執(zhí)行策略都是一種資源管理工具眉菱,最佳的執(zhí)行策略取決于可用的計算資源以及對服務(wù)質(zhì)量的需求。我們可以通過限制并發(fā)任務(wù)的數(shù)量掉分,來避免上面的兩個問題俭缓。在此,我們引入線程池的概念酥郭。
線程池华坦,構(gòu)建一定數(shù)量的工作線程,它與工作隊列密切相關(guān)不从。工作隊列保存了所有待執(zhí)行的任務(wù)惜姐,工作線程的主要工作就是從隊列中獲取任務(wù),執(zhí)行完成后返回線程池椿息,等待下一個任務(wù)歹袁。它的優(yōu)勢也非常明顯:
- 可以重用現(xiàn)有線程,減少創(chuàng)建和銷毀的頻繁開銷寝优,提高響應(yīng)性条舔。
- 線程數(shù)量可調(diào)節(jié),靈活方便乏矾,防止過多線程耗盡內(nèi)存孟抗。
總之迁杨,大多數(shù)情況下,應(yīng)該運用線程池而不是其他方式來處理并發(fā)任務(wù)夸浅。
Executor 框架的組件
說了這么多仑最,那到底 Executor 的框架是怎么樣的呢扔役?下面我們直接先來看它的框架類圖帆喇。
框架類圖
下面我們根據(jù)這個框架圖,來簡單說說各個類的情況亿胸。
(1) Executor 是一個簡單的接口坯钦,如下所示:
public interface Executor {
void execute(Runnable command);
}
(2) ExecutorService 在原有接口的基礎(chǔ)上,添加生命周期的方法侈玄,它包括三種狀態(tài):運行婉刀,關(guān)閉,終止序仙。
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
(3) ThreadPoolExecutor 框架核心類突颊,Executors 工具類最終是條用它來創(chuàng)建不同執(zhí)行策略的Executor.所以我們也可以利用它來定制機子的執(zhí)行策略。
(4) Executors 工具類潘悼,可以用它來創(chuàng)建各種各樣的 Executor.比如下面這幾種:
- newFixedThreadPool, 固定長度的線城池律秃。
- newCacheThreadPool, 可緩存的線程池,可以回收空閑線程治唤,任務(wù)多時則創(chuàng)建線程棒动,數(shù)量沒有限制。
- newSingleThreadExecutor, 單線程宾添。
- newScheduledThreadPool, 固定長度線程池船惨,可以延遲或定時執(zhí)行。
(5) ExecutorCompletionService, 它融合了Executor 和 BlockingQueue缕陕。它將計算委托給 Executor,當任務(wù)執(zhí)行結(jié)束時粱锐,結(jié)果會放入隊列里面。其實就是通過阻塞隊列作為中介來獲取結(jié)果扛邑,而不是直接使用 Future 來獲取卜范。
任務(wù)執(zhí)行的三個部分
我們先來明確任務(wù)執(zhí)行的流程。一般情況下鹿榜,任務(wù)的執(zhí)行包含包括三個部分海雪。
- 每個任務(wù)都是用 Runnable/Callable 接口的實現(xiàn)類表示。
- 任務(wù)執(zhí)行的核心是采用 Executor 框架舱殿,核心類是 ThreadPoolExecutor奥裸。
- 異步任務(wù)需要返回結(jié)果,提交任務(wù)后需要返回 Future, FutureTask 實現(xiàn)沪袭。
上面就是我們利用 Exector 框架來執(zhí)行任務(wù)的一般步驟湾宙。
框架使用示意圖
它的框架使用示意圖如下:
從圖中可以看出整個執(zhí)行的流程樟氢。但是圖上得來終覺淺,絕知此事要躬行侠鳄,還是用具體實例來演示吧埠啃。
本文完畢,具體實例的演示伟恶,請等待下一篇文章碴开。
貌似上傳的思維導(dǎo)圖不夠清晰,如果想要這篇文章更清晰的思維導(dǎo)圖博秫,請在我公號回復(fù)[Executor]獲取潦牛。