深入了解Java線程池-從設(shè)計(jì)思想到源碼解讀_第1頁
深入了解Java線程池-從設(shè)計(jì)思想到源碼解讀_第2頁
深入了解Java線程池-從設(shè)計(jì)思想到源碼解讀_第3頁
深入了解Java線程池-從設(shè)計(jì)思想到源碼解讀_第4頁
深入了解Java線程池-從設(shè)計(jì)思想到源碼解讀_第5頁
已閱讀5頁,還剩18頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論