淺談ThreadPoolExecutor

一、為什么要有線程池的概念
每次請(qǐng)求都新創(chuàng)建一個(gè)線程的話實(shí)現(xiàn)起來(lái)非常簡(jiǎn)便,但是存在一個(gè)問(wèn)題:如果并發(fā)的請(qǐng)求數(shù)量非常多,但每個(gè)線程執(zhí)行的時(shí)間很短,這樣就會(huì)頻繁的創(chuàng)建和銷毀線程,如此一來(lái)會(huì)大大降低系統(tǒng)的效率。
因此,單個(gè)任務(wù)處理時(shí)間比較短,需要處理的任務(wù)數(shù)量很大的后就需要使用線程池了。

二、使用線程池有什么好處
1.降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
2.提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。
3.提高線程的可管理性。線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低 系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。

線程池的類圖

Paste_Image.png

Executor:一個(gè)運(yùn)行新任務(wù)的簡(jiǎn)單接口;
Executor Service:擴(kuò)展了Executor接口。添加了一些用來(lái)管理執(zhí)行器生命周期和任務(wù)生命周期的方法;
Scheduled Executor Service:擴(kuò)展了Executor Service。支持Future和定期執(zhí)行任務(wù)。
三、淺談線程池
Thread Pool Executor繼承自Abstract Executor Service,也是實(shí)現(xiàn)了Executor Service接口。

Paste_Image.png

runStateOf:獲取運(yùn)行狀態(tài);
workerCountOf:獲取活動(dòng)線程數(shù);
ctlOf:獲取運(yùn)行狀態(tài)和活動(dòng)線程數(shù)的值。
...
private final Atomic Integer ctl = new Atomic Integer(ctl Of(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
...
//ctl是對(duì)線程池的運(yùn)行狀態(tài)和線程池中有效線程的數(shù)量進(jìn)行控制的一個(gè)字段, 它包含兩部分的
//信息: 線程池的運(yùn)行狀態(tài) (run State) 和線程池內(nèi)有效線程的數(shù)量 (worker Count),
//能接受新提交的任務(wù),并且也能處理阻塞隊(duì)列中的任務(wù);
//關(guān)閉狀態(tài),不再接受新提交的任務(wù),但卻可以繼續(xù)處理阻塞隊(duì)列中已保存的任務(wù)。在線程池
//處于 RUNNING 狀態(tài)時(shí),調(diào)用 shutdown()方法會(huì)使線程池進(jìn)入到該狀態(tài)。(finalize() 方法在
//執(zhí)行過(guò)程中也會(huì)調(diào)用shutdown()方法進(jìn)入該狀態(tài));
//不能接受新任務(wù),也不處理隊(duì)列中的任務(wù),會(huì)中斷正在處理任務(wù)的線程。在線程池處于 //RUNNING 或 SHUTDOWN 狀態(tài)時(shí),調(diào)用 shutdown Now() 方法會(huì)使線程池進(jìn)入到該狀態(tài);
//如果所有的任務(wù)都已終止了,worker Count (有效線程數(shù)) 為0,線程池進(jìn)入該狀態(tài)后會(huì)調(diào)用 //terminated() 方法進(jìn)入TERMINATED 狀態(tài)。
//在terminated() 方法執(zhí)行完后進(jìn)入該狀態(tài),默認(rèn)terminated()方法中什么也沒(méi)有做。

Paste_Image.png

下面仔細(xì)分析一下:

在getTask方法中,如果這時(shí)線程池的狀態(tài)是SHUTDOWN并且workQueue為空,那么就應(yīng)該返回null來(lái)結(jié)束這個(gè)工作線程,而使線程池進(jìn)入SHUTDOWN狀態(tài)需要調(diào)用shutdown方法;
shutdown方法會(huì)調(diào)用interruptIdleWorkers來(lái)中斷空閑的線程,interruptIdleWorkers持有mainLock,會(huì)遍歷workers來(lái)逐個(gè)判斷工作線程是否空閑。但getTask方法中沒(méi)有mainLock;
在getTask中,如果判斷當(dāng)前線程池狀態(tài)是RUNNING,并且阻塞隊(duì)列為空,那么會(huì)調(diào)用workQueue.take()進(jìn)行阻塞;
如果在判斷當(dāng)前線程池狀態(tài)是RUNNING后,這時(shí)調(diào)用了shutdown方法把狀態(tài)改為了SHUTDOWN,這時(shí)如果不進(jìn)行中斷,那么當(dāng)前的工作線程在調(diào)用了workQueue.take()后會(huì)一直阻塞而不會(huì)被銷毀,因?yàn)樵赟HUTDOWN狀態(tài)下不允許再有新的任務(wù)添加到workQueue中,這樣一來(lái)線程池永遠(yuǎn)都關(guān)閉不了了;
由上可知,shutdown方法與getTask方法(從隊(duì)列中獲取任務(wù)時(shí))存在競(jìng)態(tài)條件;
解決這一問(wèn)題就需要用到線程的中斷,也就是為什么要用interruptIdleWorkers方法。在調(diào)用workQueue.take()時(shí),如果發(fā)現(xiàn)當(dāng)前線程在執(zhí)行之前或者執(zhí)行期間是中斷狀態(tài),則會(huì)拋出InterruptedException,解除阻塞的狀態(tài);
但是要中斷工作線程,還要判斷工作線程是否是空閑的,如果工作線程正在處理任務(wù),就不應(yīng)該發(fā)生中斷;
所以Worker繼承自AQS,在工作線程處理任務(wù)時(shí)會(huì)進(jìn)行l(wèi)ock,interruptIdleWorkers在進(jìn)行中斷時(shí)會(huì)使用tryLock來(lái)判斷該工作線程是否正在處理任務(wù),如果tryLock返回true,說(shuō)明該工作線程當(dāng)前未執(zhí)行任務(wù),這時(shí)才可以被中斷。
下面就來(lái)分析一下interruptIdleWorkers方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 JDK中為我們提供了一個(gè)并發(fā)線程框架,它是的我們可以在有異步任務(wù)或大量并發(fā)任務(wù)需要執(zhí)行時(shí)可以使用它提供的線程...
    Justlearn閱讀 1,901評(píng)論 0 10
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,628評(píng)論 18 399
  • layout: posttitle: 《Java并發(fā)編程的藝術(shù)》筆記categories: Javaexcerpt...
    xiaogmail閱讀 6,005評(píng)論 1 19
  • 少年的假期結(jié)束了。是的,少年還在讀高中,現(xiàn)已經(jīng)高三,今天是開(kāi)學(xué)的日子,少年要去到他的學(xué)校了。 玖別墅中依舊只有少年...
    契華閱讀 335評(píng)論 0 1
  • 天一直在落雨。 不久前雨勢(shì)微弱,滴落在樹(shù)葉上留下沙沙的聲音。 而現(xiàn)在大雨滂沱,把那闌干沖刷出了細(xì)細(xì)的裂縫。 姑娘打...
    兔子說(shuō)得對(duì)閱讀 593評(píng)論 0 0

友情鏈接更多精彩內(nèi)容