1 為什么需要線程池掷贾?
- 1.在java中睛榄,使用線程來執(zhí)行異步任務(wù)時(shí),線程的創(chuàng)建和銷毀需要一定的開銷想帅。如果我們?yōu)槊恳粋€(gè)任務(wù)創(chuàng)建一個(gè)新的線程來執(zhí)行的話场靴,那么這些線程的創(chuàng)建與銷毀將消耗大量的計(jì)算資源。
- 2.為每一個(gè)任務(wù)創(chuàng)建一個(gè)新線程來執(zhí)行港准,這樣的方式可能會(huì)使處于高負(fù)荷狀態(tài)的應(yīng)用最終崩潰旨剥。
- 曙光我們將在線程池中創(chuàng)建若干條線程,當(dāng)有任務(wù)需要執(zhí)行時(shí)就從該線程池中獲取一條線程來執(zhí)行任務(wù)浅缸,如果一時(shí)間任務(wù)過多轨帜,超出線程池的線程數(shù)量,那么后面的線程任務(wù)就進(jìn)入一個(gè)等待隊(duì)列進(jìn)行等待衩椒,直到線程池有線程處于空閑時(shí)才從等待隊(duì)列獲取要執(zhí)行的任務(wù)進(jìn)行處理蚌父,以此循環(huán).....這樣就大大減少了線程創(chuàng)建和銷毀的開銷,也會(huì)緩解我們的應(yīng)用處于超負(fù)荷時(shí)的情況毛萌。
2 Executor框架的兩級(jí)調(diào)度模型
- 在java線程啟動(dòng)時(shí)會(huì)創(chuàng)建一個(gè)本地操作系統(tǒng)線程苟弛,當(dāng)該java線程終止時(shí),這個(gè)操作系統(tǒng)線程也會(huì)被回收朝聋。
- 每一個(gè)java線程都會(huì)被一對(duì)一映射為本地操作系統(tǒng)的線程嗡午,操作系統(tǒng)會(huì)調(diào)度所有的線程并將它們分別給可用的CPU。
- 所謂的映射方式是這樣實(shí)現(xiàn)的冀痕,在上層荔睹,java多線程程序通過把應(yīng)用分為若干個(gè)任務(wù)狸演,然后使用用戶級(jí)的調(diào)度器(Executor框架)將這些任務(wù)映射為固定數(shù)量的線程;在底層僻他,操作系統(tǒng)內(nèi)核將這些線程映射到硬件處理器上宵距。
從圖中我們可以看出,應(yīng)用程序通過Executor框架控制上層的調(diào)度吨拗,而下層的調(diào)度由操作系統(tǒng)內(nèi)核控制满哪,下層的調(diào)度不受應(yīng)用程序的控制。
3 Executor框架的結(jié)構(gòu)
Executor框架的結(jié)構(gòu)主要包括3個(gè)部分
- 1.任務(wù):包括被執(zhí)行任務(wù)需要實(shí)現(xiàn)的接口:Runnable接口或Callable接口
- 2.任務(wù)的執(zhí)行:包括任務(wù)執(zhí)行機(jī)制的核心接口Executor劝篷,以及繼承自Executor的EexcutorService接口哨鸭。Exrcutor有兩個(gè)關(guān)鍵類實(shí)現(xiàn)了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)。
- 3.異步計(jì)算的結(jié)果:包括接口Future和實(shí)現(xiàn)Future接口的FutureTask類
4 類間的關(guān)系UML
- Extecutor是一個(gè)接口娇妓,它是Executor框架的基礎(chǔ)像鸡,它將任務(wù)的提交與任務(wù)的執(zhí)行分離開來。
- ThreadPoolExecutor是線程池的核心實(shí)現(xiàn)類哈恰,用來執(zhí)行被提交的任務(wù)只估。
- ScheduledThreadPoolExecutor是一個(gè)實(shí)現(xiàn)類,可以在給定的延遲后運(yùn)行命令着绷,或者定期執(zhí)行命令蛔钙。ScheduledThreadPoolExecutor比Timer更靈活,功能更強(qiáng)大荠医。
- Future接口和實(shí)現(xiàn)Future接口的FutureTask類吁脱,代表異步計(jì)算的結(jié)果。
- Runnable接口和Callable接口的實(shí)現(xiàn)類子漩,都可以被ThreadPoolExecutor或者ScheduledThreadPoolExecutor執(zhí)行豫喧。區(qū)別就是Runnable無法返回執(zhí)行結(jié)果,而Callable可以返回執(zhí)行結(jié)果幢泼。
5 執(zhí)行關(guān)系
分析說明
- 創(chuàng)建任務(wù)對(duì)象
主線程首先創(chuàng)建實(shí)現(xiàn)Runnable或Callable接口的任務(wù)對(duì)象紧显,工具類Executors可以把一個(gè)Runnable對(duì)象封裝為一個(gè)Callable對(duì)象,使用如下兩種方式:
Executors.callable(Runnable task)或者Executors.callable(Runnable task,Object resule)。
- 執(zhí)行任務(wù)
把Runnable對(duì)象直接提交給ExecutorService執(zhí)行缕棵,方法為ExecutorService.execute(Runnable command)孵班;
把Runnable對(duì)象或者Callable對(duì)象提交給ExecutorService執(zhí)行,方法為ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable task)招驴。
- 注意的是如果執(zhí)行ExecutorService.submit(...),ExecutorService將返回一個(gè)實(shí)現(xiàn)Future接口的對(duì)象(其實(shí)就是FutureTask)篙程。
當(dāng)然由于FutureTask實(shí)現(xiàn)了Runnable接口,我們也可以直接創(chuàng)建FutureTask别厘,然后提交給ExecutorService執(zhí)行虱饿。
6 ExecutorService任務(wù)周期管理接口
- Java里面線程池的頂級(jí)接口是Executor,但是嚴(yán)格意義上講Executor并不是一個(gè)線程池,而只是一個(gè)執(zhí)行線程的工具氮发。
- 真正的線程池接口是ExecutorService渴肉。
- Executors類,提供了一系列工廠方法用于創(chuàng)先線程池爽冕,返回的線程池都實(shí)現(xiàn)了ExecutorService接口仇祭。
- Executor的實(shí)現(xiàn)通常都會(huì)創(chuàng)建線程來執(zhí)行任務(wù),但是使用異步方式來執(zhí)行任務(wù)時(shí)颈畸,由于之前提交任務(wù)的狀態(tài)不是立即可見的乌奇,所以如果要關(guān)閉應(yīng)用程序時(shí),就需要將受影響的任務(wù)狀態(tài)反饋給應(yīng)用程序眯娱。
- 為了解決執(zhí)行服務(wù)的生命周期問題礁苗,Executor擴(kuò)展了EecutorService接口,添加了一些用于生命周期管理的方法困乒。如下:
public interface ExecutorService extends Executor {
void shutdown();
List shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
// 省略部分方法
}
參考
https://juejin.im/entry/57ad83932e958a005442862c
https://juejin.im/entry/582185c42e958a0054feccfb