一文帶你了解Java 并發(fā)之 Executor 框架

前言

在學習 JUC 的過程中我發(fā)現(xiàn),JUC 這個包下面的文檔寫的十分的好茂装,清楚又易于理解其馏,這篇博客便是參考 JUC 中和 Executor 框架相關的一些類文檔匯總出來的。

當然了确徙,Executor 框架涉及到的類還是不少的,全部匯總的話時間成本太高执桌,有點虧鄙皇,所以這里主要就集中在了 Executor 接口及其子接口和具體實現(xiàn)上。

Executor 框架概覽

Executor 框架的起點自然就是 Executor 接口仰挣,可以說整個 Executor 框架便是建立在 Executor 接口和其子接口上的伴逸,大致結構為:

其中,接口 Executor 表示定制的 thread-like 子系統(tǒng)膘壶,包括線程池错蝴、異步 I/O 和輕量級的任務框架。同時颓芭,依賴于具體的 Executor 實現(xiàn)顷锰, 新的任務可能在新線程、已存在的任務執(zhí)行線程或調用 Executor.execute 方法的線程中執(zhí)行畜伐,不同任務的執(zhí)行可能是依序執(zhí)行的或并行執(zhí)行的馍惹。

而 Executor 的子接口 ExecutorService 提供了更完整的異步任務執(zhí)行支持躺率,每個 ExecutorService 會對任務的調度進行管理玛界,并且允許受控的 取消 操作万矾。 它的子接口 ScheduledExecutorService 還添加了對延遲和定期任務執(zhí)行的支持。

由于 Runnable 接口定義的 run 方法是沒有返回值的慎框,因此良狈,ExecutorService 還支持通過 Callable 來定義一些異步任務,這些異步任務可以有一個執(zhí)行結果笨枯, 我們可以通過相應的 Future 來獲取這個結果薪丁。

提交到 ExecutorService 的異步任務通常都會返回一個對應的 Future 對象,我們可以通過這個對象來獲取異步任務的執(zhí)行狀態(tài)和執(zhí)行結果馅精,或者取消該任務的執(zhí)行严嗜。

對于上述的接口來說,Executor 框架提供了一些默認的實現(xiàn)洲敢,很多時候漫玄,這些實現(xiàn)已經足夠我們的使用:

Executor 框架提供了兩個靈活的 可配置 的線程池實現(xiàn)

ThreadPoolExecutor(ExecutorService)?和ScheduledThreadPoolExecutor(ScheduledExecutorService)

可以通過 Executors 的工廠方法來創(chuàng)建指定配置的線程池,同時通過一些其他實用的方法來使用它們

另外压彭,類 ForkJoinPool 也提供了一個 Executor 來處理 ForkJoinTask 及其子類的實例睦优。

參考:

Executors

Executor

一般來說,我們可以手動創(chuàng)建 Thread 對象來執(zhí)行 Runnable 任務壮不,但是汗盘,在有了 Executor 框架后,更好的選擇是將這些異步任務轉交給 Executor 的具體實現(xiàn)來執(zhí)行询一。

比如說將 Thread(new(RunnableTask())).start() 替換為:

Executor executor = anExecutor;

executor.execute(new RunnableTask1());

executor.execute(new RunnableTask2());

...

當然隐孽,我們需要明白的是,不同的 Executor 實現(xiàn)是不一樣的健蕊,我們提交的異步任務不一定就在別的線程執(zhí)行缓醋,比如下面這樣的實現(xiàn):

class DirectExecutor implements Executor {

? public void execute(Runnable r) {

? ? r.run();

? }

}

但是,Executor 這個接口定義的功能很有限绊诲,同時也只支持 Runnale 形式的異步任務:

void execute(Runnable command);

參考:

Executor (Java Platform SE 8 )

ExecutorService

ExecutorService 為異步任務的執(zhí)行提供了更多的支持送粱,包括用于 終止 的方法以及可以產生用于跟蹤一個或多個異步任務進度的 Future 的方法。

首先掂之,和 Executor 不一樣的是抗俄,ExecutorService 是可以終止的,當 ExecutorService 終止后世舰,便不會接受新提交的任務动雹。可以通過兩個方法來終止 ExecutorService:

通過 shutdown 方法來終止 ExecutorService跟压,允許在執(zhí)行完先前提交的任務后在終止 ExecutorService

通過 shutdownNow 方法來終止 ExecutorService胰蝠,會阻止等待的任務啟動并嘗試停止當前正在執(zhí)行的任務

ExecutorService 終止后,就表示 ExecutorService 不存在正在或等待執(zhí)行的任務,同時茸塞,會拒絕新任務的提交躲庄,通常應該關閉未使用的 ExecutorService 以便回收資源。

然后钾虐,和 Executor.execute 方法不一樣噪窘,在 ExecutorService 中可以通過 ExecutorService.submit 方法來提交任務,這個方法會返回與提交的任務相關聯(lián)的 Future 對象效扫, 我們可以通過這個 Future 對象來等待/取消任務的執(zhí)行倔监,并獲取執(zhí)行結果。還可以通過 invokeAny 和 invokeAll 來提交一組任務菌仁,等待其中一個或所有任務的執(zhí)行浩习。

同時,相較于只支持 Runnable 的 Executor济丘,ExecutorService 還支持 Callable 形式的異步任務:

submit(Callable<T> task);

submit(Runnable task);

submit(Runnable task, T result);

參考:

ExecutorService (Java Platform SE 8 )

ScheduledExecutorService

接口 ScheduledExecutorService 相較于 ExecutorService 來說添加了對延遲和定期任務執(zhí)行的支持瘦锹,還是比較好理解的:

// 單次延遲任務

schedule(Callable<V> callable, long delay, TimeUnit unit)

schedule(Runnable command, long delay, TimeUnit unit)

// 循環(huán)延遲任務

scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

參考:

ScheduledExecutorService (Java Platform SE 8

)

ThreadPoolExecutor

ThreadPoolExecutor 是 ExecutorService 的一種具體實現(xiàn),一般情況下我們可以通過 Executors 來創(chuàng)建新的線程池闪盔,但是弯院,了解 ThreadPoolExecutor 提供的各配置項還是很有用的, 而 ThreadPoolExecutor 文檔中對這些配置項給出了很詳細的描述泪掀。

Core and maximum pool sizes - 線程池核心線程數(shù)和最大線程數(shù)听绳,線程池根據這兩個參數(shù)來自動調整線程池的大小:

在提交新任務且運行的線程數(shù)少于 corePoolSize 時异赫,即使其他工作線程處于空閑狀態(tài)椅挣,也會創(chuàng)建一個新線程來處理新任務

在運行的線程數(shù)大于 corePoolSize 但小于 maximumPoolSize 時,就僅在 隊列已滿 時才創(chuàng)建新線程

這兩個參數(shù)可以在創(chuàng)建線程池時設置塔拳,也可以在創(chuàng)建后動態(tài)修改

On-demand construction - ThreadPoolExecutor 默認情況下是在新任務提交后在創(chuàng)建啟動線程鼠证,但是可以通過覆蓋 prestartCoreThread() 或 prestartAllCoreThreads() 改變這一行為, 這在初始隊列不為空時會很有用靠抑。

Creating new threads - ThreadPoolExecutor 通過 ThreadFactory 來創(chuàng)建新的線程量九,默認情況下會使用 Executors.defaultThreadFactory(), 這個 ThreadFactory 創(chuàng)建的所有線程擁有相同的 ThreadGroup 和 NORM_PRIORITY 級別的優(yōu)先級, 同時也是非守護線程颂碧。

Keep-alive times - 當當前線程池中的線程數(shù)超過 corePoolSize 時荠列,多余的線程將在閑置時間超過 keepAliveTime 時終止。默認情況下參數(shù) keepAliveTime 僅在線程數(shù)超過 corePoolSize 時起作用载城, 但是也可以通過 allowCoreThreadTimeOut(boolean) 方法讓核心線程在閑置一段時間后也被終止肌似。

Queuing - 任意的 BlockingQueue 都可以用于傳輸和保留提交的任務,隊列的使用和當前線程池的大小相關:

在當前線程池中的線程數(shù)小于 corePoolSize 時诉瓦,優(yōu)先創(chuàng)建新的線程

在當前線程池中的線程數(shù)超過 corePoolSize 時川队,優(yōu)先選擇將任務放入隊列

在新的任務 無法放入隊列 且線程數(shù)小于 maximumPoolSize 時力细,會創(chuàng)建新的線程,否則會拒絕新的任務

線程池中一般可以選擇下面三種隊列使用策略:

直接交付固额,比如說使用 SynchronousQueue

隊列眠蚂,直接將任務傳遞給工作線程,如果沒有合適的工作線程來處理任務对雪,那么就會選擇創(chuàng)建新的線程獲拒絕任務河狐,這時一般會將

maximumPoolSize 設的大一點

無界隊列米绕,比如說使用 LinkedBlockingQueue 隊列瑟捣,這種情況下因為新的任務必然可以放入隊列,因此栅干,參數(shù)maximumPoolSize 便失去了意義迈套,此時最多只會有 corePoolSize 個線程在運行

有界隊列,比如說使用 ArrayBlockingQueue 隊列碱鳞,這時我們可以通過靈活調整 corePoolSize,

maximumPoolSize 和隊列大小來更加充分的利用線程池

Rejected tasks - 當 ExecutorService 被關閉或者任務無法放入隊列且線程數(shù)量超過 maximumPoolSize 時桑李,新任務的提交會被拒絕,這時便會調用 RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor) 來處理被拒絕的任務窿给, 可選的處理策略有:

ThreadPoolExecutor.AbortPolicy(default) - 拋出運行時異常 RejectedExecutionException

ThreadPoolExecutor.CallerRunsPolicy - 在調用 executor 的 線程執(zhí)行該任務

ThreadPoolExecutor.DiscardPolicy - 直接刪除忽略新的任務

ThreadPoolExecutor.DiscardOldestPolicy - 如果 ExecutorService沒有被關閉贵白,那么就丟棄隊列頭的任務重新提交這個任務

Hook methods - 方法 beforeExecute(Thread, Runnable) 和 afterExecute(Runnable, Throwable) 會在每個任務執(zhí)行的前后調用,也可以覆蓋 terminated() 方法在 Executor 終止 后 執(zhí)行一些額外的操作崩泡。

Queue maintenance - 可以通過 getQueue() 方法訪問工作隊列禁荒,以進行監(jiān)視和調試。強烈建議不要將此方法用于任何其他目的角撞,可以通過 remove(Runnable) 和 purge() 清理隊列中的任務呛伴。

Finalization - 在線程池不在被引用 且 沒有剩余工作線程時,線程池將會被關閉谒所∪瓤担可以考慮將 corePoolSize 設小并通過 allowCoreThreadTimeOut(boolean) 保證核心線程閑置久了也會被回收, 那么劣领,忘記調用 shutdown 也不要擔心資源的浪費了姐军。

這么多的配置項,如此強大的功能尖淘,我只想說庶弃,Doug Lea NB(破音)!5鲁骸歇攻!

附,文檔上的一個例子:

// 可以暫停的線程池

class PausableThreadPoolExecutor extends ThreadPoolExecutor {

? private boolean isPaused;

? private ReentrantLock pauseLock = new ReentrantLock();

? private Condition unpaused = pauseLock.newCondition();

? public PausableThreadPoolExecutor(...) { super(...); }

? protected void beforeExecute(Thread t, Runnable r) {

? ? super.beforeExecute(t, r);

? ? pauseLock.lock();

? ? try {

? ? ? while (isPaused) unpaused.await();

? ? } catch (InterruptedException ie) {

? ? ? t.interrupt();

? ? } finally {

? ? ? pauseLock.unlock();

? ? }

? }

? public void pause() {

? ? pauseLock.lock();

? ? try {

? ? ? isPaused = true;

? ? } finally {

? ? ? pauseLock.unlock();

? ? }

? }

? public void resume() {

? ? pauseLock.lock();

? ? try {

? ? ? isPaused = false;

? ? ? unpaused.signalAll();

? ? } finally {

? ? ? pauseLock.unlock();

? ? }

? }

}

參考:

ThreadPoolExecutor (Java Platform SE 8 )

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 繼承了 ThreadPoolExecutor 并實現(xiàn)了 ScheduledExecutorService 接口梆造,是比 Timer 更好的選擇缴守。它使用指定大小的 corePoolSize 和無界隊列葬毫, 因此,參數(shù) maximumPoolSize 沒有意義屡穗。

ScheduledThreadPoolExecutor 能夠保證任務的執(zhí)行時間 不早于 指定的時間贴捡,但是不能保證一定在那個時間執(zhí)行。對于指定在同一時間執(zhí)行的任務村砂,將會按照 FIFO 的規(guī)則執(zhí)行烂斋。

另外,假如在開始執(zhí)行前任務就已經被取消了础废,那么 ScheduledThreadPoolExecutor 就不會在執(zhí)行那個任務汛骂,但是默認不會從隊列中取出該任務。 但是可以通過 setRemoveOnCancelPolicy(boolean) 要求 ScheduledThreadPoolExecutor 在任務取消時就立即從隊列中取出該任務评腺。

參考:

ScheduledThreadPoolExecutor (Java Platform SE 8 )

Executors

Executors 提供了一些針對 Executor, ExecutorService, ScheduledExecutorService 和 ThreadFactory 的實用方法帘瞭,文檔上的話就沒有什么好說的了,其實就是一個熟悉接口的問題蒿讥。

最后

上面都是自己整理好的蝶念!我就把資料貢獻出來給有需要的人!順便求一波關注芋绸,哈哈~各位小伙伴關注我后私信【Java】就可以免費領取噠

原文作者:花生炒花生

原文鏈接:https://juejin.im/post/5e522e7551882549422ed54b

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末媒殉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子摔敛,更是在濱河造成了極大的恐慌廷蓉,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舷夺,死亡現(xiàn)場離奇詭異苦酱,居然都是意外死亡,警方通過查閱死者的電腦和手機给猾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門疫萤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人敢伸,你說我怎么就攤上這事扯饶。” “怎么了池颈?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵尾序,是天一觀的道長。 經常有香客問我躯砰,道長每币,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任琢歇,我火速辦了婚禮兰怠,結果婚禮上梦鉴,老公的妹妹穿的比我還像新娘。我一直安慰自己揭保,他們只是感情好肥橙,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秸侣,像睡著了一般存筏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上味榛,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天椭坚,我揣著相機與錄音,去河邊找鬼励负。 笑死藕溅,一個胖子當著我的面吹牛匕得,可吹牛的內容都是我干的继榆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼汁掠,長吁一口氣:“原來是場噩夢啊……” “哼略吨!你這毒婦竟也來了?” 一聲冷哼從身側響起考阱,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤翠忠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后乞榨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秽之,經...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年吃既,在試婚紗的時候發(fā)現(xiàn)自己被綠了考榨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鹦倚,死狀恐怖河质,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情震叙,我是刑警寧澤掀鹅,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站媒楼,受9級特大地震影響乐尊,放射性物質發(fā)生泄漏。R本人自食惡果不足惜划址,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一扔嵌、第九天 我趴在偏房一處隱蔽的房頂上張望昏滴。 院中可真熱鬧,春花似錦对人、人聲如沸谣殊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姻几。三九已至,卻和暖如春势告,著一層夾襖步出監(jiān)牢的瞬間蛇捌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工咱台, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留络拌,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓回溺,卻偏偏與公主長得像春贸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子遗遵,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容