1、ThreadPoolExecutor類
java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個(gè)類,因此如果要透徹地了解Java中的線程池,必須先了解這個(gè)類。下面我們來看一下ThreadPoolExecutor類的具體實(shí)現(xiàn)源碼。
在ThreadPoolExecutor類中提供了四個(gè)構(gòu)造方法:
public?class?ThreadPoolExecutor?extends?AbstractExecutorService {
????.....
????public?ThreadPoolExecutor(int?corePoolSize,int?maximumPoolSize,long?keepAliveTime,TimeUnit unit,
????????????BlockingQueue workQueue);
????public?ThreadPoolExecutor(int?corePoolSize,int?maximumPoolSize,long?keepAliveTime,TimeUnit unit,
????????????BlockingQueue workQueue,ThreadFactory threadFactory);
????public?ThreadPoolExecutor(int?corePoolSize,int?maximumPoolSize,long?keepAliveTime,TimeUnit unit,
????????????BlockingQueue workQueue,RejectedExecutionHandler handler);
????public?ThreadPoolExecutor(int?corePoolSize,int?maximumPoolSize,long?keepAliveTime,TimeUnit unit,
????????BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
????...
}
從上面的代碼可以得知,ThreadPoolExecutor繼承了AbstractExecutorService類,并提供了四個(gè)構(gòu)造器,事實(shí)上,通過觀察每個(gè)構(gòu)造器的源碼具體實(shí)現(xiàn),發(fā)現(xiàn)前面三個(gè)構(gòu)造器都是調(diào)用的第四個(gè)構(gòu)造器進(jìn)行的初始化工作。
下面解釋下一下構(gòu)造器中各個(gè)參數(shù)的含義:
(1)corePoolSize:核心池的大小,這個(gè)參數(shù)跟后面講述的線程池的實(shí)現(xiàn)原理有非常大的關(guān)系。在創(chuàng)建了線程池后,默認(rèn)情況下,線程池中并沒有任何線程,在等待有任務(wù)到來才創(chuàng)建線程去執(zhí)行任務(wù),除非調(diào)用了prestartAllCoreThreads()或者prestartCoreThread()方法,預(yù)創(chuàng)建線程的意思,即在沒有任務(wù)到來之前就創(chuàng)建corePoolSize個(gè)線程,并且在創(chuàng)建完線程后都會(huì)保持線程數(shù)。如果希望線程在一定存活時(shí)間后自動(dòng)銷毀,則可調(diào)用allowCoreThreadTimeOut()方法。默認(rèn)情況下,在創(chuàng)建了線程池后,線程池中的線程數(shù)為0,當(dāng)有任務(wù)來之后,就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行任務(wù),當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后,就會(huì)把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中;
(2)maximumPoolSize:線程池最大線程數(shù),這個(gè)參數(shù)也是一個(gè)非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個(gè)線程;
(3)keepAliveTime:表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會(huì)終止。默認(rèn)情況下,只有當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí),keepAliveTime才會(huì)起作用,直到線程池中的線程數(shù)不大于corePoolSize,即當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí),如果一個(gè)線程空閑的時(shí)間達(dá)到keepAliveTime,則會(huì)終止,直到線程池中的線程數(shù)不超過corePoolSize;
(4)unit:參數(shù)keepAliveTime的時(shí)間單位,有7種取值,在TimeUnit類中有7種靜態(tài)屬性:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小時(shí)
TimeUnit.MINUTES; //分鐘
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS;//毫秒
TimeUnit.MICROSECONDS;//微妙
TimeUnit.NANOSECONDS; //納秒
(5)workQueue:一個(gè)阻塞隊(duì)列,用來存儲(chǔ)等待執(zhí)行的任務(wù),這個(gè)參數(shù)的選擇也很重要,會(huì)對(duì)線程池的運(yùn)行過程產(chǎn)生重大影響,一般來說,這里的阻塞隊(duì)列有以下幾種選擇:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊(duì)策略與BlockingQueue有關(guān)。
(6)threadFactory:線程工廠,主要用來創(chuàng)建線程;
(7)handler:表示當(dāng)拒絕處理任務(wù)時(shí)的策略,有以下四種取值:
ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù),但是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)
具體參數(shù)的配置與線程池的關(guān)系將在下一節(jié)講述。
從上面給出的ThreadPoolExecutor類的代碼可以知道,ThreadPoolExecutor繼承了AbstractExecutorService,我們來看一下AbstractExecutorService的實(shí)現(xiàn):
public?abstract?class?AbstractExecutorService?implements?ExecutorService {
????protected? RunnableFuture newTaskFor(Runnable runnable, T value) { };
????protected? RunnableFuture newTaskFor(Callable callable) { };
????public?Future submit(Runnable task) {};
????public? Future submit(Runnable task, T result) { };
????public? Future submit(Callable task) { };
????private? T doInvokeAny(Collection> tasks,?boolean?timed,?long?nanos)? throws?InterruptedException, ExecutionException,TimeoutException {};
????public? T invokeAny(Collection> tasks)?throws?InterruptedException, ExecutionException {};
????public? T invokeAny(Collection> tasks,?long?timeout, TimeUnit unit) throws?InterruptedException, ExecutionException, TimeoutException { };
????public? List> invokeAll(Collection> tasks)?throws?InterruptedException { };
????public? List> invokeAll(Collection> tasks,?long?timeout, TimeUnit unit) throws?InterruptedException {};
}
AbstractExecutorService是一個(gè)抽象類,它實(shí)現(xiàn)了ExecutorService接口。
我們接著看ExecutorService接口的實(shí)現(xiàn):
public?interface?ExecutorService?extends?Executor {
????void?shutdown();
????boolean?isShutdown();
????boolean?isTerminated();
????boolean?awaitTermination(long?timeout, TimeUnit unit) throws?InterruptedException;
???? Future submit(Callable task);
???? Future submit(Runnable task, T result);
????Future submit(Runnable task);
???? List> invokeAll(Collection> tasks)?throws?InterruptedException;
???? List> invokeAll(Collection> tasks,?long?timeout, TimeUnit unit)? throws?InterruptedException;
???? T invokeAny(Collection> tasks)?throws?InterruptedException, ExecutionException;
???? T invokeAny(Collection> tasks,?long?timeout, TimeUnit unit) throws?InterruptedException, ExecutionException, TimeoutException;
}
而ExecutorService又是繼承了Executor接口,我們看一下Executor接口的實(shí)現(xiàn):
public?interface?Executor {
????void?execute(Runnable command);
}
到這里,大家應(yīng)該明白了ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor幾個(gè)之間的關(guān)系了。
Executor是一個(gè)頂層接口,在它里面只聲明了一個(gè)方法execute(Runnable),返回值為void,參數(shù)為Runnable類型,從字面意思可以理解,就是用來執(zhí)行傳進(jìn)去的任務(wù)的;
然后ExecutorService接口繼承了Executor接口,并聲明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象類AbstractExecutorService實(shí)現(xiàn)了ExecutorService接口,基本實(shí)現(xiàn)了ExecutorService中聲明的所有方法;
然后ThreadPoolExecutor繼承了類AbstractExecutorService。
在ThreadPoolExecutor類中有幾個(gè)非常重要的方法:
execute()
submit()
shutdown()
shutdownNow()
execute()方法實(shí)際上是Executor中聲明的方法,在ThreadPoolExecutor進(jìn)行了具體的實(shí)現(xiàn),這個(gè)方法是ThreadPoolExecutor的核心方法,通過這個(gè)方法可以向線程池提交一個(gè)任務(wù),交由線程池去執(zhí)行。
submit()方法是在ExecutorService中聲明的方法,在AbstractExecutorService就已經(jīng)有了具體的實(shí)現(xiàn),在ThreadPoolExecutor中并沒有對(duì)其進(jìn)行重寫,這個(gè)方法也是用來向線程池提交任務(wù)的,但是它和execute()方法不同,它能夠返回任務(wù)執(zhí)行的結(jié)果,去看submit()方法的實(shí)現(xiàn),會(huì)發(fā)現(xiàn)它實(shí)際上還是調(diào)用的execute()方法,只不過它利用了Future來獲取任務(wù)執(zhí)行結(jié)果(Future相關(guān)內(nèi)容將在下一篇講述)。
shutdown()和shutdownNow()是用來關(guān)閉線程池的。
還有很多其他的方法:
比如:getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等獲取與線程池相關(guān)屬性的方法,有興趣的朋友可以自行查閱API。
2、深入剖析線程池實(shí)現(xiàn)原理
在上一節(jié)我們從宏觀上介紹了ThreadPoolExecutor,下面我們來深入解析一下線程池的具體實(shí)現(xiàn)原理,將從下面幾個(gè)方面講解:
? ? ? ?1.線程池狀態(tài)
2.任務(wù)的執(zhí)行
3.線程池中的線程初始化
4.任務(wù)緩存隊(duì)列及排隊(duì)策略
5.任務(wù)拒絕策略
6.線程池的關(guān)閉
7.線程池容量的動(dòng)態(tài)調(diào)整
(1)線程池生命周期
在ThreadPoolExecutor中將運(yùn)行狀態(tài)存儲(chǔ)在高階位上,另外定義了5個(gè)static final變量表示線程池的各個(gè)生命周期:
private static final int RUNNING = -1 << COUNT_BITS; //運(yùn)行狀態(tài)
private static final int SHUTDOWN = 0 << COUNT_BITS; //不接受新任務(wù),但在處理排隊(duì)的任務(wù)。
private static final int STOP = 1 << COUNT_BITS; //不接受新的任務(wù),也不處理隊(duì)列中的任務(wù),中斷正在進(jìn)行的任務(wù)
private static final int TIDYING = 2 << COUNT_BITS; //所有的任務(wù)終止,隊(duì)列中的任務(wù)也為零,線程池為tidying,將調(diào)用terminated()
private static final int TERMINATED = 3 << COUNT_BITS; //調(diào)用terminated()方法完成后
當(dāng)創(chuàng)建線程池后,初始時(shí),線程池處于RUNNING狀態(tài);
RUNNING -> SHUTDOWN:在調(diào)用shutdown()時(shí),可能隱式地在finalize()中
(RUNNING or SHUTDOWN) -> STOP:調(diào)用shutdownNow()
SHUTDOWN -> TIDYING:當(dāng)線程池還有隊(duì)列中的任務(wù)都為空時(shí)
STOP -> TIDYING:當(dāng)線程池為空時(shí)
TIDYING -> TERMINATED:當(dāng)調(diào)用?terminated()方法完成時(shí)
(2)任務(wù)的執(zhí)行
在了解將任務(wù)提交給線程池到任務(wù)執(zhí)行完畢整個(gè)過程之前,我們先來看一下ThreadPoolExecutor類中其他的一些比較重要成員變量:
private?final?BlockingQueue workQueue;?//任務(wù)緩存隊(duì)列,用來存放等待執(zhí)行的任務(wù)
private?final?ReentrantLock mainLock =?new?ReentrantLock();?//線程池的主要狀態(tài)鎖,對(duì)線程池狀態(tài)(比如線程池大小 、runState等)的改變都要使用這個(gè)鎖
private?final?HashSet workers =?new?HashSet();?//用來存放工作集
private?volatile?long?keepAliveTime;?//線程存貨時(shí)間???
private?volatile?boolean?allowCoreThreadTimeOut;?//是否允許為核心線程設(shè)置存活時(shí)間
private?volatile?int?corePoolSize;?//核心池的大?。淳€程池中的線程數(shù)目大于這個(gè)參數(shù)時(shí),提交的任務(wù)會(huì)被放進(jìn)任務(wù)緩存隊(duì)列)
private?volatile?int?maximumPoolSize;?//線程池最大能容忍的線程數(shù)
private?volatile?int?poolSize;?//線程池中當(dāng)前的線程數(shù)
private?volatile?RejectedExecutionHandler handler;?//任務(wù)拒絕策略
private?volatile?ThreadFactory threadFactory;?//線程工廠,用來創(chuàng)建線程
private?int?largestPoolSize;?//用來記錄線程池中曾經(jīng)出現(xiàn)過的最大線程數(shù)
private?long?completedTaskCount;?//用來記錄已經(jīng)執(zhí)行完畢的任務(wù)個(gè)數(shù)
? ? 每個(gè)變量的作用都已經(jīng)標(biāo)明出來了,這里要重點(diǎn)解釋一下corePoolSize、maximumPoolSize、largestPoolSize三個(gè)變量。
corePoolSize在很多地方被翻譯成核心池大小,其實(shí)我的理解這個(gè)就是線程池的大小。舉個(gè)簡(jiǎn)單的例子:
假如有一個(gè)工廠,工廠里面有10個(gè)工人,每個(gè)工人同時(shí)只能做一件任務(wù)。
因此只要當(dāng)10個(gè)工人中有工人是空閑的,來了任務(wù)就分配給空閑的工人做;
當(dāng)10個(gè)工人都有任務(wù)在做時(shí),如果還來了任務(wù),就把任務(wù)進(jìn)行排隊(duì)等待;
如果說新任務(wù)數(shù)目增長(zhǎng)的速度遠(yuǎn)遠(yuǎn)大于工人做任務(wù)的速度,那么此時(shí)工廠主管可能會(huì)想補(bǔ)救措施,比如重新招4個(gè)臨時(shí)工人進(jìn)來;
然后就將任務(wù)也分配給這4個(gè)臨時(shí)工人做;
如果說著14個(gè)工人做任務(wù)的速度還是不夠,此時(shí)工廠主管可能就要考慮不再接收新的任務(wù)或者拋棄前面的一些任務(wù)了。
當(dāng)這14個(gè)工人當(dāng)中有人空閑時(shí),而新任務(wù)增長(zhǎng)的速度又比較緩慢,工廠主管可能就考慮辭掉4個(gè)臨時(shí)工了,只保持原來的10個(gè)工人,畢竟請(qǐng)額外的工人是要花錢的。
這個(gè)例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。
也就是說corePoolSize就是線程池大小,maximumPoolSize在我看來是線程池的一種補(bǔ)救措施,即任務(wù)量突然過大時(shí)的一種補(bǔ)救措施。
不過為了方便理解,在本文后面還是將corePoolSize翻譯成核心池大小。
largestPoolSize只是一個(gè)用來起記錄作用的變量,用來記錄線程池中曾經(jīng)有過的最大線程數(shù)目,跟線程池的容量沒有任何關(guān)系。
下面我們進(jìn)入正題,看一下任務(wù)從提交到最終執(zhí)行完畢經(jīng)歷了哪些過程。
在ThreadPoolExecutor類中,最核心的任務(wù)提交方法是execute()方法,雖然通過submit也可以提交任務(wù),但是實(shí)際上submit方法里面最終調(diào)用的還是execute()方法,所以我們只需要研究execute()方法的實(shí)現(xiàn)原理即可:
public void execute(Runnable command) {
if (command ==null)
throw new NullPointerException();
? ? int c =ctl.get();
? ? if (workerCountOf(c)
if (addWorker(command, true))
????return;
? ? ? ? c =ctl.get();
? ? }
if (isRunning(c) &&workQueue.offer(command)) {
int recheck =ctl.get();
? ? ? ? if (!isRunning(recheck) && remove(command))
reject(command);
? ? ? ? else if (workerCountOf(recheck) ==0)
addWorker(null, false);
? ? }
else if (!addWorker(command, false))
reject(command);
}
方法注釋:
?Proceed in 3 steps:
1. If fewer than corePoolSize threads are running, try to start a new thread with the given command as its first task.? The call to addWorker atomically checks runState and workerCount, and so prevents false alarms that would add threads when it shouldn't, by returning false.
當(dāng)運(yùn)行線程小于corePoolSize?數(shù)量時(shí),開啟一個(gè)新的線程去運(yùn)行第一個(gè)任務(wù)。調(diào)用addWorker?方法原子性的檢查runState和workerCount
,防止錯(cuò)誤提醒。
2. If a task can be successfully queued, then we still need to double-check whether we should have added a thread (because existing ones died since last checking) or that the pool shut down since entry into this method. So we recheck state and if necessary roll back the enqueuing if stopped, or start a new thread if there are none.
如果任務(wù)可以成功排隊(duì),那么我們?nèi)匀恍枰屑?xì)檢查是否應(yīng)該添加一個(gè)線程(因?yàn)樽陨洗螜z查后現(xiàn)有的線程已經(jīng)死亡),或者自從進(jìn)入此方法后池關(guān)閉了。 所以我們重新檢查狀態(tài),如果沒有運(yùn)行則回滾入隊(duì)并拒絕該任務(wù),或者如果沒有則啟動(dòng)新的線程。
?3. If we cannot queue task, then we try to add a new thread.? If it fails, we know we are shut down or saturated and so reject the task.
如果我們不能排隊(duì)任務(wù),那么我們嘗試添加一個(gè)新線程。 如果失敗,我們知道我們已關(guān)閉或飽和,因此拒絕該任務(wù)。
(3)線程池中的線程初始化
默認(rèn)情況下,創(chuàng)建線程池之后,線程池中是沒有線程的,需要提交任務(wù)之后才會(huì)創(chuàng)建線程。
在實(shí)際中如果需要線程池創(chuàng)建之后立即創(chuàng)建線程,可以通過以下兩個(gè)方法辦到:
prestartCoreThread():初始化一個(gè)核心線程;
prestartAllCoreThreads():初始化所有核心線程
public boolean prestartCoreThread() {
????return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true);
}
public int prestartAllCoreThreads() {
????int n = 0;
????while (addWorker(null, true))
????????++n;
????return n;
}
(4)任務(wù)緩存隊(duì)列及排隊(duì)策略
在前面我們多次提到了任務(wù)緩存隊(duì)列,即workQueue,它用來存放等待執(zhí)行的任務(wù)。
workQueue的類型為BlockingQueue,通常可以取下面三種類型:
1)ArrayBlockingQueue:基于數(shù)組的先進(jìn)先出隊(duì)列,此隊(duì)列創(chuàng)建時(shí)必須指定大??;
2)LinkedBlockingQueue:基于鏈表的先進(jìn)先出隊(duì)列,如果創(chuàng)建時(shí)沒有指定此隊(duì)列大小,則默認(rèn)為Integer.MAX_VALUE;
3)synchronousQueue:這個(gè)隊(duì)列比較特殊,它不會(huì)保存提交的任務(wù),而是將直接新建一個(gè)線程來執(zhí)行新來的任務(wù)。
(5)任務(wù)拒絕策略
當(dāng)線程池的任務(wù)緩存隊(duì)列已滿并且線程池中的線程數(shù)目達(dá)到maximumPoolSize,如果還有任務(wù)到來就會(huì)采取任務(wù)拒絕策略,通常有以下四種策略:
ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù),但是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)
(6)線程池的關(guān)閉
ThreadPoolExecutor提供了兩個(gè)方法,用于線程池的關(guān)閉,分別是shutdown()和shutdownNow(),其中:
shutdown():不會(huì)立即終止線程池,而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止,但再也不會(huì)接受新的任務(wù)
shutdownNow():立即終止線程池,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊(duì)列,返回尚未執(zhí)行的任務(wù)
(7)線程池容量的動(dòng)態(tài)調(diào)整
ThreadPoolExecutor提供了動(dòng)態(tài)調(diào)整線程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),
setCorePoolSize:設(shè)置核心池大小
setMaximumPoolSize:設(shè)置線程池最大能創(chuàng)建的線程數(shù)目大小
當(dāng)上述參數(shù)從小變大時(shí),ThreadPoolExecutor進(jìn)行線程賦值,還可能立即創(chuàng)建新的線程來執(zhí)行任務(wù)。
總結(jié):在jdk8中線程池主要使用了CAS無鎖方法,重復(fù)校驗(yàn)方式,跟方便閱讀代碼并速度提升
參考:https://www.cnblogs.com/dolphin0520/p/3932921.html