Executor框架的兩級調度模型
在HotSpot VM的線程模型中砚偶,Java線程(java.lang.Thread)被一對一映射為本地操作系統(tǒng)線程来候。Java線程啟動時會創(chuàng)建一個本地操作系統(tǒng)線程致讥;當該Java線程終止時,這個操作系統(tǒng)線程也會被回收名船。操作系統(tǒng)會調度所有線程并將它們分配給可用的CPU幔虏。
在上層,Java多線程程序通常把應用分解為若干個任務捂掰,然后使用用戶級的調度器
應用程序通過Executor框架控制上層的調度姐帚;而下層的調度由操作系統(tǒng)內核控制,下層的調度不受應用程序的控制膳汪。
1.Executor框架的結構
Executor框架主要由3大部分組成如下:
1、任務遗嗽。包括被執(zhí)行任務需要實現的接口:Runnable接口或Callable接口。
2痹换、任務的執(zhí)行。包括任務執(zhí)行機制的核心接口Executor娇豫,以及繼承自Executor的
ExecutorService接口。Executor框架有兩個關鍵類實現了ExecutorService接口
(ThreadPoolExecutor和ScheduledThreadPoolExecutor)锤躁。
3、異步計算的結果或详。包括接口Future和實現Future接口的FutureTask類系羞。
下面是這些類和接口的簡介。
·Executor是一個接口霸琴,它是Executor框架的基礎椒振,它將任務的提交與任務的執(zhí)行分離開來。
·ThreadPoolExecutor是線程池的核心實現類梧乘,用來執(zhí)行被提交的任務澎迎。
·ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲后運行命令选调,或者定期執(zhí)行命令夹供。ScheduledThreadPoolExecutor比Timer更靈活,功能更強大仁堪。
Future接口和實現Future接口的FutureTask類哮洽,代表異步計算的結果。
·Runnable和Callable接口實現類都可被ThreadPoolExecutor或Scheduled-ThreadPoolExecutor執(zhí)行弦聂。
下面來看下使用示意圖:
主線程首先要創(chuàng)建實現Runnable或者Callable接口的任務對象鸟辅。工具類Executors可以把一
個Runnable對象封裝為一個Callable對象(Executors.callable(Runnable task)或
Executors.callable(Runnable task,Object resule))莺葫。
然后可以把Runnable對象直接交給ExecutorService執(zhí)行(ExecutorService.execute(Runnable
command))匪凉;或者也可以把Runnable對象或Callable對象提交給ExecutorService執(zhí)行(Executor-
Service.submit(Runnable task)或ExecutorService.submit(Callable<T>task))。
如果執(zhí)行ExecutorService.submit(…)捺檬,ExecutorService將返回一個實現Future接口的對象
(到目前為止的JDK中再层,返回的是FutureTask對象)。由于FutureTask實現了Runnable,程序員也可
以創(chuàng)建FutureTask树绩,然后直接交給ExecutorService執(zhí)行萨脑。
最后,主線程執(zhí)行FutureTask.get()方法來等待任務執(zhí)行完成饺饭。主線程也可以執(zhí)行FutureTask.cancel(boolean mayInterruptIfRunning)來取消此任務的執(zhí)行渤早。
Executor框架的成員
主要介紹Executor框架的主要成員:ThreadPoolExecutor、ScheduledThreadPoolExecutor瘫俊、
Future接口鹊杖、Runnable接口、Callable接口和Executors扛芽。
(1)ThreadPoolExecutor(想深入理解實現原理可以看這篇文章:https://juejin.im/post/5d67e5b4e51d4561f64a0849)
ThreadPoolExecutor通常使用工廠類Executors來創(chuàng)建。Executors可以創(chuàng)建3種類型的
ThreadPoolExecutor:SingleThreadExecutor川尖、FixedThreadPool和CachedThreadPool。
下面分別介紹這3種ThreadPoolExecutor被芳。
(1) ThreadPoolExecutor
(1)FixedThreadPool畔濒。
是Executors提供的侵状,創(chuàng)建使用固定線程數的FixedThreadPool的API趣兄。
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactorythreadFactory
FixedThreadPool適用于為了滿足資源管理的需求诽俯,而需要限制當前線程數量的應用場景承粤,它適用于負載比較重的服務器辛臊。
(2)SingleThreadExecutor
下面是Executors提供的彻舰,創(chuàng)建使用單個線程的SingleThread-Executor的API。
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
SingleThreadExecutor適用于需要保證順序地執(zhí)行各個任務隔心;并且在任意時間點硬霍,不會有多個線程是活動的應用場景唯卖。
(3)CachedThreadPool拜轨。
下面是Executors提供的,創(chuàng)建一個會根據需要創(chuàng)建新線程的CachedThreadPool的API卵沉。
public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
CachedThreadPool是大小無界的線程池偎箫,適用于執(zhí)行很多的短期異步任務的小程序,或者是負載較輕的服務器恶复。
(2)ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor通常使用工廠類Executors來創(chuàng)建谤牡。Executors可以創(chuàng)建2種類型的ScheduledThreadPoolExecutor.
如下:
·ScheduledThreadPoolExecutor翅萤。包含若干個線程的ScheduledThreadPoolExecutor腊满。
·SingleThreadScheduledExecutor碳蛋。只包含一個線程的ScheduledThreadPoolExecutor肃弟。
下面是工廠類Executors提供的,創(chuàng)建固定個數線程的ScheduledThreadPoolExecutor的API穷缤。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize,ThreadFactory)
ScheduledThreadPoolExecutor適用于需要多個后臺線程執(zhí)行周期任務津肛,同時為了滿足資源管理的需求而需要限制后臺線程的數量的應用場景快耿。
下面是Executors提供的掀亥,創(chuàng)建單個線程的SingleThreadScheduledExecutor的API
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
SingleThreadScheduledExecutor適用于需要單個后臺線程執(zhí)行周期任務搪花,同時需要保證順序地執(zhí)行各個任務的應用場景撮竿。
(3)Future接口
Future接口和實現Future接口的FutureTask類用來表示異步計算的結果。當我們把Runnable
接口或Callable接口的實現類提交(submit)給ThreadPoolExecutor或ScheduledThreadPoolExecutor時髓需,ThreadPoolExecutor或ScheduledThreadPoolExecutor會向我們返回一個FutureTask對象僚匆。
<T> Future<T> submit(Callable<T> task)
<T> Future<T> submit(Runnable task, T result)
Future<> submit(Runnable task)
FutureTask是Future的子類咧擂。
有一點需要讀者注意松申,到目前最新的JDK8為止贸桶,Java通過上述API返回的是一個FutureTask對象刨啸。但從API可以看到识脆,Java僅僅保證返回的是一個實現了Future接口的對象。在將來的JDK實現中换团,返回的可能不一定是FutureTask宫蛆。
(4)Runnable接口和Callable接口
Runnable接口和Callable接口的實現類耀盗,都可以被ThreadPoolExecutor或Scheduled-
ThreadPoolExecutor執(zhí)行叛拷。它們之間的區(qū)別是Runnable不會返回結果忿薇,而Callable可以返回結
果。
除了可以自己創(chuàng)建實現Callable接口的對象外揉燃,還可以使用工廠類Executors來把一個
Runnable包裝成一個Callable炊汤。
下面是Executors提供的抢腐,把一個Runnable包裝成一個Callable的API。
public static Callable<Object> callable(Runnable task)
下面是Executors提供的婿着,把一個Runnable和一個待返回的結果包裝成一個Callable的API醋界。
public static <T> Callable<T> callable(Runnable task, T result) // 假設返回對象Callable2
當我們把一個Callable對象(比如上面的Callable1或Callable2)提交給
ThreadPoolExecutor或ScheduledThreadPoolExecutor執(zhí)行時,submit(…)會向我們返回一個
FutureTask對象丘侠。我們可以執(zhí)行FutureTask.get()方法來等待任務執(zhí)行完成。
當任務成功完成后FutureTask.get()將返回該任務的結果蜗字。例如,如果提交的是對象Callable1挪捕,FutureTask.get()方法將返回null;如果提交的是對象Callable2级零,FutureTask.get()方法將返回result對象。
ThreadPoolExecutor詳解
可以看這兩篇文章:
從源碼來看JDK8線程池ThreadPoolExecutor的實現原理(一)
https://juejin.im/post/5d67e5b4e51d4561f64a0849
從源碼來看JDK8線程池ThreadPoolExecutor的實現原理(二)
https://juejin.im/post/5d688686e51d4561ce5a1c8b
·通過Executor框架的工具類Executors鉴嗤,可以創(chuàng)建3種類型的ThreadPoolExecutor。
·FixedThreadPool醉锅。
·SingleThreadExecutor。
·CachedThreadPool荣挨。
下面將分別介紹這3種ThreadPoolExecutor
FixedThreadPool詳解
FixedThreadPool被稱為可重用固定線程數的線程池朴摊。下面是FixedThreadPool的源代碼實現。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool的corePoolSize和maximumPoolSize都被設置為創(chuàng)建FixedThreadPool時指定的參數nThreads甚纲。
FixedThreadPool的execute()方法的運行示意圖:
1)如果當前運行的線程數少于corePoolSize介杆,則創(chuàng)建新線程來執(zhí)行任務。
2)在線程池完成預熱之后(當前運行的線程數等于corePoolSize)荆隘,將任務加入LinkedBlockingQueue椰拒。
3)線程執(zhí)行完1中的任務后凰荚,會在循環(huán)中反復從LinkedBlockingQueue獲取任務來執(zhí)行便瑟。
FixedThreadPool使用無界隊列LinkedBlockingQueue作為線程池的工作隊列(隊列的容量為
Integer.MAX_VALUE)到涂。
FixedThreadPool使用無界隊列作為工作隊列會對線程池帶來如下影響:
1)當線程池中的線程數達到corePoolSize后颁督,新任務將在無界隊列中等待适篙,因此線程池中
的線程數不會超過corePoolSize嚷节。
2)由于1虎锚,使用無界隊列時maximumPoolSize將是一個無效參數窜护。
3)由于1和2柱徙,使用無界隊列時keepAliveTime將是一個無效參數。
4)由于使用無界隊列敌完,運行中的FixedThreadPool(未執(zhí)行方法shutdown()或
shutdownNow())不會拒絕任務(不會調用RejectedExecutionHandler.rejectedExecution方法)滨溉。
SingleThreadExecutor詳解
SingleThreadExecutor是使用單個worker線程的Executor晦攒。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
SingleThreadExecutor的corePoolSize和maximumPoolSize被設置為1得哆。其他參數與
FixedThreadPool相同贩据。SingleThreadExecutor使用無界隊列LinkedBlockingQueue作為線程池的工
作隊列(隊列的容量為Integer.MAX_VALUE)乐设。SingleThreadExecutor使用無界隊列作為工作隊列
對線程池帶來的影響與FixedThreadPool相同.
執(zhí)行流程:
1)如果當前運行的線程數少于corePoolSize(即線程池中無運行的線程)近尚,則創(chuàng)建一個新線
程來執(zhí)行任務戈锻。
2)在線程池完成預熱之后(當前線程池中有一個運行的線程),將任務加入Linked-
BlockingQueue哈街。
3)線程執(zhí)行完1中的任務后拒迅,會在一個無限循環(huán)中反復從LinkedBlockingQueue獲取任務來
執(zhí)行璧微。
3、CachedThreadPool詳解
CachedThreadPool是一個會根據需要創(chuàng)建新線程的線程池胞得。下面是創(chuàng)建CachedThread-Pool的源代碼阶剑。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
CachedThreadPool的corePoolSize被設置為0危号,即corePool為空葱色;maximumPoolSize被設置為Integer.MAX_VALUE苍狰,即maximumPool是無界的。這里把keepAliveTime設置為60L俐填,意味著CachedThreadPool中的空閑線程等待新任務的最長時間為60秒英融,空閑線程超過60秒后將會被終止驶悟。FixedThreadPool和SingleThreadExecutor使用無界隊列LinkedBlockingQueue作為線程池的工作隊列痕鳍。
CachedThreadPool使用沒有容量的SynchronousQueue作為線程池的工作隊列笼呆,但CachedThreadPool的maximumPool是無界的。這意味著汗茄,如果主線程提交任務的速度高于maximumPool中線程處理任務的速度時洪碳,CachedThreadPool會不斷創(chuàng)建新線程偶宫。極端情況下纯趋,CachedThreadPool會因為創(chuàng)建過多線程而耗盡CPU和內存資源吵冒。