




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第深入了解Java線程池:從設(shè)計(jì)思想到源碼解讀publicvoidrun(){
log.debug("線程名稱:{},正在執(zhí)行task:{}",Thread.currentThread().getName(),taskNum);
try{
//模擬其他操作
Thread.currentThread().sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
log.debug("task{}執(zhí)行完畢",taskNum);
}
輸出:
codedata-type="codeline"14:08:07.166[javatv-1]DEBUGayue-線程名稱:javatv-1,正在執(zhí)行task:1/codecodedata-type="codeline"14:08:07.166[javatv-2]DEBUGayue-線程名稱:javatv-2,正在執(zhí)行task:3/codecodedata-type="codeline"14:08:08.170[javatv-1]DEBUGayue-task1執(zhí)行完畢/codecodedata-type="codeline"14:08:08.170[javatv-2]DEBUGayue-task3執(zhí)行完畢/codecodedata-type="codeline"14:08:08.170[javatv-1]DEBUGayue-線程名稱:javatv-1,正在執(zhí)行task:2/codecodedata-type="codeline"14:08:09.172[javatv-1]DEBUGayue-task2執(zhí)行完畢/code
拒絕策略
線程池提供了四種策略:
1.AbortPolicy,直接拋出異常,默認(rèn)策略。
2.CallerRunsPolicy,用調(diào)用者所在的線程來執(zhí)行任務(wù)。
3.DiscardOldestPolicy,丟棄阻塞隊(duì)列中靠最前的任務(wù),并執(zhí)行當(dāng)前任務(wù)。
4.DiscardPolicy,直接丟棄任務(wù)。、
把上面代碼的循環(huán)次數(shù)改為4次,則會(huì)拋出java.util.concurrent.RejectedExecutionException異常。
for(inti=1;ii++){
executor.execute(newMyTask(i));
}
關(guān)閉線程池
可以通過調(diào)用線程池的shutdown或shutdownNow方法來關(guān)閉線程池。它們的原理是遍歷線程池中的工作線程,然后逐個(gè)調(diào)用線程的interrupt方法來中斷線程,所以無法響應(yīng)中斷的任務(wù)可能永遠(yuǎn)無法終止。但是它們存在一定的區(qū)別,shutdownNow首先將線程池的狀態(tài)設(shè)置成STOP,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程,并返回等待執(zhí)行任務(wù)的列表,而shutdown只是將線程池的狀態(tài)設(shè)置成SHUTDOWN狀態(tài),然后中斷所有沒有正在執(zhí)行任務(wù)的線程。簡單來說:
shutdown():線程池狀態(tài)變?yōu)镾HUTDOWN,不會(huì)接收新任務(wù),但已提交任務(wù)會(huì)執(zhí)行完,不會(huì)阻塞調(diào)用線程的執(zhí)行。
shutdownNow():線程池狀態(tài)變?yōu)镾TOP,會(huì)接收新任務(wù),會(huì)將隊(duì)列中的任務(wù)返回,并用interrupt的方式中斷正在執(zhí)行的任務(wù)。
只要調(diào)用了這兩個(gè)關(guān)閉方法中的任意一個(gè),isShutdown方法就會(huì)返回true。當(dāng)所有的任務(wù)都已關(guān)閉后,才表示線程池關(guān)閉成功,這時(shí)調(diào)用isTerminaed方法會(huì)返回true。至于應(yīng)該調(diào)用哪一種方法來關(guān)閉線程池,應(yīng)該由提交到線程池的任務(wù)特性決定,通常調(diào)用shutdown方法來關(guān)閉線程池,如果任務(wù)不一定要執(zhí)行完,則可以調(diào)用shutdownNow方法。
Executors靜態(tài)工廠
Executors,提供了一系列靜態(tài)工廠方法用于創(chuàng)建各種類型的線程池,基于ThreadPoolExecutor。
1.FixedThreadPool
publicstaticExecutorServicenewFixedThreadPool(intnThreads){
returnnewThreadPoolExecutor(nThreads,nThreads,
0L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueueRunnable
}
特點(diǎn):核心線程數(shù)等于最大線程數(shù),因此也無需超時(shí)時(shí)間,執(zhí)行完立即回收,阻塞隊(duì)列是無界的,可以放任意數(shù)量的任務(wù)。
場(chǎng)景:適用于任務(wù)量已知,相對(duì)耗時(shí)的任務(wù)。
2.newCachedThreadPool
publicstaticExecutorServicenewFixedThreadPool(intnThreads){
returnnewThreadPoolExecutor(nThreads,nThreads,
0L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueueRunnable
}
可根據(jù)需要?jiǎng)?chuàng)建新線程的線程池,如果現(xiàn)有線程沒有可用的,則創(chuàng)建一個(gè)新線程并添加到池中,如果有被使用完但是還沒銷毀的線程,就復(fù)用該線程。終止并從緩存中移除那些已有60秒鐘未被使用的線程。因此,長時(shí)間保持空閑的線程池不會(huì)使用任何資源。這種線程池比較靈活,對(duì)于執(zhí)行很多短期異步任務(wù)的程序而言,這些線程池通??商岣叱绦蛐阅?。
特點(diǎn):核心線程數(shù)是0,最大線程數(shù)是Integer.MAX_VALUE,全部都是空閑線程60s后回收。
場(chǎng)景:執(zhí)行大量、耗時(shí)少的任務(wù)。
3.newSingleThreadExecutor
publicstaticExecutorServicenewSingleThreadExecutor(){
returnnewFinalizableDelegatedExecutorService
(newThreadPoolExecutor(1,1,
0L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueueRunnable()));
}
特點(diǎn):單線程線程池。希望多個(gè)任務(wù)排隊(duì)執(zhí)行,線程數(shù)固定為1,任務(wù)數(shù)多于1時(shí),會(huì)放入無界隊(duì)列排隊(duì),任務(wù)執(zhí)行完畢,這唯一的線程也不會(huì)被釋放。
場(chǎng)景:區(qū)別于自己創(chuàng)建一個(gè)單線程串行執(zhí)行任務(wù),如果使用newThread任務(wù)執(zhí)行失敗而終止那么沒有任何補(bǔ)救措施,而線程池還會(huì)新建一個(gè)線程,保證池的正常工作。
4.ScheduledThreadPool
publicstaticScheduledExecutorServicenewScheduledThreadPool(intcorePoolSize){
returnnewScheduledThreadPoolExecutor(corePoolSize);
}
ScheduledThreadPoolExecutor繼承自ThreadPoolExecutor。它主要用來在給定的延遲之后運(yùn)行任務(wù),或者定期執(zhí)行任務(wù)。ScheduledThreadPoolExecuto的功能與Timer類似,但ScheduledThreadPoolExecutor功能更強(qiáng)大、更靈活。Timer對(duì)應(yīng)的是單個(gè)后臺(tái)線程,而ScheduledThreadPoolExecutor可以在構(gòu)造函數(shù)中指定多個(gè)對(duì)應(yīng)的后臺(tái)線程數(shù)。
特點(diǎn):核心線程數(shù)量固定,非核心線程數(shù)量無限,執(zhí)行完閑置10ms后回收,任務(wù)隊(duì)列為延時(shí)阻塞隊(duì)列。
場(chǎng)景:執(zhí)行定時(shí)或周期性的任務(wù)。
合理地配置線程池
需要針對(duì)具體情況而具體處理,不同的任務(wù)類別應(yīng)采用不同規(guī)模的線程池,任務(wù)類別可劃分為CPU密集型任務(wù)、IO密集型任務(wù)和混合型任務(wù)。
CPU密集型任務(wù):線程池中線程個(gè)數(shù)應(yīng)盡量少,不應(yīng)大于CPU核心數(shù);
IO密集型任務(wù):由于IO操作速度遠(yuǎn)低于CPU速度,那么在運(yùn)行這類任務(wù)時(shí),CPU絕大多數(shù)時(shí)間處于空閑狀態(tài),那么線程池可以配置盡量多些的線程,以提高CPU利用率;
混合型任務(wù):可以拆分為CPU密集型任務(wù)和IO密集型任務(wù),當(dāng)這兩類任務(wù)執(zhí)行時(shí)間相差無幾時(shí),通過拆分再執(zhí)行的吞吐率高于串行執(zhí)行的吞吐率,但若這兩類任務(wù)執(zhí)行時(shí)間有數(shù)據(jù)級(jí)的差距,那么沒有拆分的意義。
線程池的監(jiān)控
如果在系統(tǒng)中大量使用線程池,則有必要對(duì)線程池進(jìn)行監(jiān)控,方便在出現(xiàn)問題時(shí),可以根據(jù)線程池的使用狀況快速定位問題。利用線程池提供的參數(shù)進(jìn)行監(jiān)控,參數(shù)如下:
taskCount:線程池需要執(zhí)行的任務(wù)數(shù)量。
completedTaskCount:線程池在運(yùn)行過程中已完成的任務(wù)數(shù)量,小于或等于taskCount。
largestPoolSize:線程池曾經(jīng)創(chuàng)建過的最大線程數(shù)量,通過這個(gè)數(shù)據(jù)可以知道線程池是否滿過。如等于線程池的最大大小,則表示線程池曾經(jīng)滿了。
getPoolSize:線程池的線程數(shù)量。如果線程池不銷毀的話,池里的線程不會(huì)自動(dòng)銷毀,所以這個(gè)大小只增不減。
getActiveCount:獲取活動(dòng)的線程數(shù)。
通過擴(kuò)展線程池進(jìn)行監(jiān)控:繼承線程池并重寫線程池的beforeExecute(),afterExecute()和terminated()方法,可以在任務(wù)執(zhí)行前、后和線程池關(guān)閉前自定義行為。如監(jiān)控任務(wù)的平均執(zhí)行時(shí)間,最大執(zhí)行時(shí)間和最小執(zhí)行時(shí)間等。
源碼分析
在使用線程池的時(shí)候,我其實(shí)有一些問題也隨之而來,比如線程池的線程怎么創(chuàng)建?任務(wù)怎么執(zhí)行?任務(wù)怎么分配?線程執(zhí)行完后怎么辦?是存活還是死亡?什么時(shí)候死亡?為什么要使用阻塞隊(duì)列等等問題。帶著這些問題,我們?nèi)プx讀源碼,讀源碼怎么入手?通過ThreadPoolExecutor的execute()方法。submit底層也是調(diào)用了execute()。
execute
publicvoidexecute(Runnablecommand){
//如果沒有任務(wù)直接拋出異常
if(command==null)
thrownewNullPointerException();
//獲取當(dāng)前線程的狀態(tài)+線程個(gè)數(shù)
intc=ctl.get();
*workerCountOf,線程池當(dāng)前線程數(shù),并判斷是否小于核心線程數(shù)
if(workerCountOf(c)corePoolSize){//如果小于
if(addWorker(command,true))
return;
c=ctl.get();
if(isRunning(c)workQueue.offer(command)){
//這里是向任務(wù)隊(duì)列投放任務(wù)成功,對(duì)線程池的運(yùn)行中狀態(tài)做二次檢查
//如果線程池二次檢查狀態(tài)是非運(yùn)行中狀態(tài),則從任務(wù)隊(duì)列移除當(dāng)前的任務(wù)調(diào)用拒絕策略處理(也就是移除前面成功入隊(duì)的任務(wù)實(shí)例)
intrecheck=ctl.get();
if(!isRunning(recheck)remove(command))
reject(command);
/*走到下面的elseif分支,說明有以下的前提:
*1、待執(zhí)行的任務(wù)已經(jīng)成功加入任務(wù)隊(duì)列
*2、線程池可能是RUNNING狀態(tài)
*3、傳入的任務(wù)可能從任務(wù)隊(duì)列中移除失?。ㄒ瞥〉奈ㄒ豢赡芫褪侨蝿?wù)已經(jīng)被執(zhí)行了)
*如果當(dāng)前工作線程數(shù)量為0,則創(chuàng)建一個(gè)非核心線程并且傳入的任務(wù)對(duì)象為null-返回
*也就是創(chuàng)建的非核心線程不會(huì)馬上運(yùn)行,而是等待獲取任務(wù)隊(duì)列的任務(wù)去執(zhí)行
*如果前工作線程數(shù)量不為0,原來應(yīng)該是最后的else分支,但是可以什么也不做,
*因?yàn)槿蝿?wù)已經(jīng)成功入隊(duì)列,總會(huì)有合適的時(shí)機(jī)分配其他空閑線程去執(zhí)行它。
elseif(workerCountOf(recheck)==0)
addWorker(null,false);
/*走到這里說明有以下的前提:
*1、線程池中的工作線程總數(shù)已經(jīng)大于等于corePoolSize(簡單來說就是核心線程已經(jīng)全部懶創(chuàng)建完畢)
*2、線程池可能不是RUNNING狀態(tài)
*3、線程池可能是RUNNING狀態(tài)同時(shí)任務(wù)隊(duì)列已經(jīng)滿了
*如果向任務(wù)隊(duì)列投放任務(wù)失敗,則會(huì)嘗試創(chuàng)建非核心線程傳入任務(wù)執(zhí)行
*創(chuàng)建非核心線程失敗,此時(shí)需要拒絕執(zhí)行任務(wù)
elseif(!addWorker(command,false))
reject(command);
}
addWorker
第一個(gè)if判斷線程池當(dāng)前線程數(shù)是否小于核心線程數(shù)。
if(workerCountOf(c)corePoolSize){
if(addWorker(command,true))
return;
c=ctl.get();
}
如果小于,則進(jìn)入addWorker方法:
privatebooleanaddWorker(RunnablefirstTask,booleancore){
retry:
//外層循環(huán):判斷線程池狀態(tài)
for(;;){
intc=ctl.get();
//獲取線程池狀態(tài)
intrs=runStateOf(c);
//檢查線程池的狀態(tài)是否存活.
if(rs=SHUTDOWN!(rs==SHUTDOWNfirstTask==null!workQueue.isEmpty()))
returnfalse;
//內(nèi)層循環(huán):線程池添加核心線程并返回是否添加成功的結(jié)果
for(;;){
//線程數(shù)量
intwc=workerCountOf(c);
//線程數(shù)量超過容量,返回false
if(wc=CAPACITY||wc=(corecorePoolSize:maximumPoolSize))
returnfalse;
//CAS增加線程數(shù)量,若成功跳出外層循環(huán)
if(compareAndIncrementWorkerCount(c))
breakretry;
//否則失敗,并更新c
c=ctl.get();//Re-readctl
//如果這時(shí)的線程池狀態(tài)發(fā)生變化,重新對(duì)外層循環(huán)進(jìn)行自旋
if(runStateOf(c)!=rs)
continueretry;
//elseCASfailedduetoworkerCountchange;retryinnerloop
//如果CAS成功了,則繼續(xù)往下走
booleanworkerStarted=false;
booleanworkerAdded=false;
Workerw=null;
try{
//創(chuàng)建一個(gè)Worker,這個(gè)Worker實(shí)現(xiàn)了Runable,把它看成一個(gè)任務(wù)單元
w=newWorker(firstTask);
//這個(gè)Thread就是當(dāng)前的任務(wù)單元Worker,即this
finalThreadt=w.thread;
if(t!=null){
//加鎖,因?yàn)榭赡苡卸鄠€(gè)線程來調(diào)用
finalReentrantLockmainLock=this.mainLock;
mainLock.lock();
try{
//再次檢查線程池的狀態(tài),避免在獲取鎖前調(diào)用shutdown方法
intrs=runStateOf(ctl.get());
if(rsSHUTDOWN||(rs==SHUTDOWNfirstTask==null)){
//如果t線程已經(jīng)啟動(dòng)尚未終止,則拋出異常
if(t.isAlive())//precheckthattisstartable
thrownewIllegalThreadStateException();
//否則,加入線程池
workers.add(w);
ints=workers.size();
if(slargestPoolSize)
largestPoolSize=s;
workerAdded=true;
}finally{
mainLock.unlock();
//加入線程池后,啟動(dòng)該線程,上面已經(jīng)設(shè)置為true
if(workerAdded){
t.start();
workerStarted=true;
}finally{
//如果線程啟動(dòng)失敗,則調(diào)用addWorkerFailed,回滾操作
if(!workerStarted)
addWorkerFailed(w);
returnworkerStarted;
}
Worker
Worker是ThreadPoolExecutor的內(nèi)部類,繼承了AQS并且實(shí)現(xiàn)了Runnable。
privatefinalclassWorkerextendsAbstractQueuedSynchronizerimplementsRunnable{
finalThreadthread;
/**Initialtasktorun.Possiblynull.*/
RunnablefirstTask;
//構(gòu)造方法
Worker(RunnablefirstTask){
//在調(diào)用runWorker前禁止中斷
//當(dāng)其它線程調(diào)用了線程池的shutdownNow時(shí)候,如果worker狀態(tài)=0則會(huì)中斷該線程
//具體方法在interruptIfStarted()中可以看到
setState(-1);//inhibitinterruptsuntilrunWorker
this.firstTask=firstTask;
this.thread=getThreadFactory().newThread(this);
/**DelegatesmainrunlooptoouterrunWorker*/
publicvoidrun(){
runWorker(this);
//省略其他代碼...
}
可以看到,在Worker的構(gòu)造方法可以知道,其中的thread屬性就是通過this去創(chuàng)建的,所以線程池核心線程的創(chuàng)建主要是run方法中的runWorker方法:
runWorker
runWorker核心線程執(zhí)行邏輯。
finalvoidrunWorker(Workerw){
Threadwt=Thread.currentThread();
Runnabletask=w.firstTask;
w.firstTask=null;
//調(diào)用unlock()是為了讓外部可以中斷
w.unlock();//allowinterrupts
//線程退出的原因,true是任務(wù)導(dǎo)致,false是線程正常退出
booleancompletedAbruptly=true;
try{
//1.如果firstTask不為null,則執(zhí)行firstTask
//2.如果firstTask為null,則調(diào)用getTask()從隊(duì)列獲取任務(wù)
//3.阻塞隊(duì)列的特性就是:當(dāng)隊(duì)列為空時(shí),當(dāng)前線程會(huì)被阻塞等待
while(task!=null||(task=getTask())!=null){
w.lock();
//判斷線程池的狀態(tài),如果線程池正在停止,則對(duì)當(dāng)前線程進(jìn)行中斷操作
if((runStateAtLeast(ctl.get(),STOP)||(Terrupted()
runStateAtLeast(ctl.get(),STOP)))!wt.isInterrupted())
errupt();//中斷
try{
//該方法里面沒有內(nèi)容,可以自己擴(kuò)展實(shí)現(xiàn),比如上面提到的線程池的監(jiān)控
beforeExecute(wt,task);
Throwablethrown=null;
try{
//執(zhí)行具體的任務(wù)
task.run();
}catch(RuntimeExceptionx){//線程異常后操作
thrown=x;throwx;
}catch(Errorx){
thrown=x;throwx;
}catch(Throwablex){
thrown=x;thrownewError(x);
}finally{
//同beforeExecute()
afterExecute(task,thrown);
}finally{
task=null;//helpgc
//統(tǒng)計(jì)當(dāng)前worker完成了多少個(gè)任務(wù)
pletedTasks++;
//釋放鎖
w.unlock();
completedAbruptly=false;
}finally{
//處理線程退出,completedAbruptly為true說明由于任務(wù)異常導(dǎo)致線程非正常退出
processWorkerExit(w,completedAbruptly);
}
getTask
而對(duì)于其中的getTask()方法,任務(wù)隊(duì)列中的任務(wù)調(diào)度給空閑線程,該方法是非常重要的,為什么重要?其中就涉及到面試官常問的線程池如何保證核心線程不會(huì)被銷毀,而空閑線程會(huì)被銷毀?
privateRunnablegetTask(){
//判斷最新一次的poll是否超時(shí)
//poll:取走BlockingQueue里排在首位的對(duì)象
booleantimedOut=false;//Didthelastpoll()timeout
for(;;){
intc=ctl.get();
intrs=runStateOf(c);
//Checkifqueueemptyonlyifnecessary.
*條件1:線程池狀態(tài)SHUTDOWN、STOP、TERMINATED狀態(tài)
*條件2:線程池STOP、TERMINATED狀態(tài)或workQueue為空
*條件1與條件2同時(shí)為true,則workerCount-1,并且返回null
*注:條件2是考慮到SHUTDOWN狀態(tài)的線程池不會(huì)接受任務(wù),但仍會(huì)處理任務(wù)(前面也講到了)
if(rs=SHUTDOWN(rs=STOP||workQueue.isEmpty())){
decrementWorkerCount();
returnnull;
intwc=workerCountOf(c);
//Areworkerssubjecttoculling
*該屬性的作用是判斷當(dāng)前線程是否允許超時(shí):
*1.allowCoreThreadTimeOut
*如果為false(默認(rèn)),核心線程即使在空閑時(shí)也保持活動(dòng)狀態(tài)。
*如果為true,則核心線程使用keepAliveTime超時(shí)等待工作。
*2.wccorePoolSize
*當(dāng)前線程是否已經(jīng)超過核心線程數(shù)量。
booleantimed=allowCoreThreadTimeOut||wccorePoolSize;
*判斷當(dāng)前線程是否可以退出:
*1.wcmaximumPoolSize||(timedtimedOut)
*wcmaximumPoolSize=true,說明當(dāng)前的工作線程總數(shù)大于線程池最大線程數(shù)。
*timedtimedOut=true,說明當(dāng)前線程允許超時(shí)并且已經(jīng)超時(shí)。
*2.wc1||workQueue.isEmpty()
*工作線程總數(shù)大于1或者任務(wù)隊(duì)列為空,則通過CAS把線程數(shù)減去1,同時(shí)返回null
if((wcmaximumPoolSize||(timedtimedOut))(wc1||workQueue.isEmpty())){
if(compareAndDecrementWorkerCount(c))
returnnull;
continue;
try{
*1.poll(longtimeout,TimeUnitunit):從BlockingQueue取出一個(gè)隊(duì)首的對(duì)象,
*如果在指定時(shí)間內(nèi),隊(duì)列一旦有數(shù)據(jù)可取,則立即返回隊(duì)列中的數(shù)據(jù)。否則直到時(shí)間超時(shí)還沒有數(shù)據(jù)可取,返回失敗。
*2.take():取走BlockingQueue里排在首位的對(duì)象,若BlockingQueue為空,阻斷進(jìn)入等待狀態(tài)直到BlockingQueue有新的數(shù)據(jù)被加入。
*如果timed為true,通過poll()方法做超時(shí)拉取,keepAliveTime時(shí)間內(nèi)沒有等待到有效的任務(wù),則返回null。
*如果timed為false,通過take()做阻塞拉取,會(huì)阻塞到有下一個(gè)有效的任務(wù)時(shí)候再返回(一般不會(huì)是null)。
Runnabler=timedworkQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS):
workQueue.take();
if(r!=null)
returnr;
//通過poll()方法從任務(wù)隊(duì)列中拉取任務(wù)為null
timedOut=true;
}catch(InterruptedExceptionretry){
timedOut=false;
}
①對(duì)于getTask()下面的這段代碼,這段邏輯大多數(shù)情況下是針對(duì)非核心線程:
booleantimed=allowCoreThreadTimeOut||wccorePoolSize;
if((wcmaximumPoolSize||(timedtimedOut))(wc1||workQueue.isEmpty())){
if(compareAndDecrementWorkerCount(c))
returnnull;
continue;
}
②我們這樣來閱讀這段代碼,當(dāng)工作線程數(shù)大于核心線程corePoolSize,此時(shí)進(jìn)入execute()方法中的第二個(gè)if語句:
if(isRunning(c)workQueue.offer(command)){
intrecheck=ctl.get();
if(!isRunning(recheck)remove(command))
reject(command);
elseif(workerCountOf(recheck)==0)
addWorker(null,false);
}
此時(shí)線程池總數(shù)已經(jīng)超過了corePoolSize但小于maximumPoolSize,當(dāng)任務(wù)隊(duì)列已經(jīng)滿了的時(shí)候,會(huì)通過addWorker(task,false)添加非核心線程。
而在高并發(fā)的情況下,肯定會(huì)產(chǎn)生多余的線程,也就是出現(xiàn)①中的情況wcmaximumPoolSize,而這些多余的線程怎么辦,是不是會(huì)被回收?如果workQueue.poll沒有獲取到有效的任務(wù),那么①中的邏輯剛好與addWorker(task,false)相反,通過CAS減少非核心線程,使得工作線程總數(shù)趨向于corePoolSize。
如果對(duì)于非核心線程,上一輪循環(huán)獲取任務(wù)對(duì)象為null,在默認(rèn)情況下allowCoreThreadTimeOut=false,因此,getTask()中timed=true,如果沒有獲取到任務(wù),此時(shí)timedOut=true,這一輪循環(huán)很容易滿足timedtimedOut為true,這個(gè)時(shí)候getTask()返回null會(huì)導(dǎo)致Worker#runWorker()方法跳出死循環(huán),之后執(zhí)行processWorkerExit()方法處理后續(xù)工作,而該非核心線程對(duì)應(yīng)的Worker則變成游離對(duì)象,等待被JVM回收。
當(dāng)allowCoreThreadTimeOut設(shè)置為true的時(shí)候,這里分析的非核心線程的生命周期終結(jié)邏輯同時(shí)會(huì)適用于核心線程。
由此推出一個(gè)面試題:線程池有多個(gè)線程同時(shí)沒取到任務(wù),會(huì)全部回收嗎?
舉個(gè)例子:線程池核心線程數(shù)是5,最大線程數(shù)為5,當(dāng)前工作線程數(shù)為6(65,意味著當(dāng)前可以觸發(fā)線程回收),如果此時(shí)有3個(gè)線程同時(shí)超時(shí)沒有獲取到任務(wù),這3個(gè)線程會(huì)都被回收銷毀嗎?
思路:這道題的核心點(diǎn)在于有多個(gè)線程同時(shí)超時(shí)獲取不到任務(wù)。正常情況下,此時(shí)會(huì)觸發(fā)線程回收的流程。但是我們知道,正常不設(shè)置allowCoreThreadTimeOut變量時(shí),線程池即使沒有任務(wù)處理,也會(huì)保持核心線程數(shù)的線程。如果這邊3個(gè)線程被全部回收,那此時(shí)線程數(shù)就變成了3個(gè),不符合核心線程數(shù)5個(gè),所以這邊我們可以首先得出答案:不會(huì)被全部回收。這個(gè)時(shí)候面試官肯定會(huì)問為什么?
根據(jù)答案不難推測(cè),為了防止本題的這種并發(fā)回收問題的出現(xiàn),線程回收的流程必然會(huì)有并發(fā)控制。compareAndDecrementWorkerCount(c)用的是CAS方法,如果CAS失敗就continue,進(jìn)入下一輪循環(huán),重新判斷。
像上述例子,其中一條線程會(huì)CAS失敗,然后重新進(jìn)入循環(huán),發(fā)現(xiàn)工作線程數(shù)已經(jīng)只有5了,timed=false,這條線程就不會(huì)被銷毀,可以一直阻塞了,此時(shí)就會(huì)調(diào)用workQueue.take()阻塞等待下一次的任務(wù),也就是說核心線程并不會(huì)死亡。
從這里也可以看出,雖然有核心線程數(shù),但線程并沒有區(qū)分是核心還是非核心,并不是先創(chuàng)建的就是核心,超過核心線程數(shù)后創(chuàng)建的就是非核心,最終保留哪些線程,完全隨機(jī)。
然后可以回答出前面的問題,線程池如何保證核心線程不會(huì)被銷毀,而空閑線程會(huì)被銷毀?
核心線程是因?yàn)檎{(diào)用了阻塞方法而不會(huì)被銷毀,空閑線程調(diào)用了超時(shí)方法在下次執(zhí)行時(shí)獲取不到任務(wù)而死亡。
這樣回答其實(shí)是可以的,但是這可能顯示出你是背得八股文,所以你應(yīng)該回答核心線程不僅僅是因?yàn)檎{(diào)用了阻塞方法而不會(huì)被銷毀,同時(shí)利用了CAS來保證。
還可以得出getTask()返回null的情況:
1.線程池的狀態(tài)已經(jīng)為STOP,TIDYING,TERMINATED,或者是SHUTDOWN且工作隊(duì)列為空。
2.工作線程數(shù)大于最大線程數(shù)或當(dāng)前工作線程已超時(shí),且,其存在工作線程或任務(wù)隊(duì)列為空。
runWorker的流程:
processWorkerExit
在runWorker的finally塊中,當(dāng)任務(wù)執(zhí)行之后,要對(duì)其做處理,作線程在執(zhí)行完processWorkerExit()方法才算真正的終結(jié),該方法如下:
privatevoidprocessWorkerExit(Workerw,booleancompletedAbruptly){
//因?yàn)閽伋鲇脩舢惓?dǎo)致線程終結(jié),直接使工作線程數(shù)減1即可
//如果沒有任何異常拋出的情況下是通過getTask()返回null引導(dǎo)線程正常跳出runWorker()方法的while死循環(huán)從而正常終結(jié),這種情況下,在getTask()中已經(jīng)把線程數(shù)減1
if(completedAbruptly)//Ifabrupt,thenworkerCountwasn'tadjusted
decrementWorkerCount();
finalReentrantLockmainLock=this.mainLock;
mainLock.lock();
try{
//全局的已完成任
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 森林生態(tài)補(bǔ)償與林權(quán)流轉(zhuǎn)合作協(xié)議
- 網(wǎng)絡(luò)影視作品版權(quán)代理合作協(xié)議
- 綠色建筑電氣系統(tǒng)設(shè)計(jì)、安裝及性能驗(yàn)收協(xié)議
- 重型工業(yè)儀器定期校準(zhǔn)認(rèn)證與風(fēng)險(xiǎn)評(píng)估合同
- 生物制藥潔凈廠房空氣凈化系統(tǒng)租賃與安全檢測(cè)合同
- 航空貨運(yùn)代理航空貨物清關(guān)代理承包協(xié)議
- 海外留學(xué)行李保險(xiǎn)與全球托運(yùn)質(zhì)量監(jiān)管協(xié)議
- 《社會(huì)保險(xiǎn)知識(shí)普及教學(xué)課件》
- 《慢性腎衰竭》課件2
- 《農(nóng)業(yè)機(jī)械操作培訓(xùn)》課件
- 臨床類面試真題及答案
- 夫妻間借款協(xié)議合同
- 【8地一模 初二會(huì)考】2025年安徽省亳州市利辛縣中考一模地理試題(含解析)
- ktv服務(wù)員合同協(xié)議書范本
- 2025年中國心電電極片市場(chǎng)調(diào)查研究報(bào)告
- 監(jiān)管方式(貿(mào)易方式)與征免性質(zhì)與征免方式對(duì)應(yīng)關(guān)系及其代碼和解釋
- 廣東省退休年齡新規(guī)定
- 2025-2030醫(yī)療設(shè)備器械行業(yè)市場(chǎng)發(fā)展分析及投資前景研究報(bào)告
- 城投公司競聘試題及答案
- 西部計(jì)劃面試題目及答案
- 公文筐的測(cè)試題及答案
評(píng)論
0/150
提交評(píng)論