前言
在學習 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 提供了一些針對 Executor, ExecutorService, ScheduledExecutorService 和 ThreadFactory 的實用方法帘瞭,文檔上的話就沒有什么好說的了,其實就是一個熟悉接口的問題蒿讥。
最后
上面都是自己整理好的蝶念!我就把資料貢獻出來給有需要的人!順便求一波關注芋绸,哈哈~各位小伙伴關注我后私信【Java】就可以免費領取噠
原文作者:花生炒花生
原文鏈接:https://juejin.im/post/5e522e7551882549422ed54b