java線程池ThreadPoolExecutor實(shí)現(xiàn)原理詳解
前言
做java開(kāi)發(fā)的,一般都避免不了要面對(duì)java線程池技術(shù),像tomcat之類(lèi)的容器天然就支持多線程。
即使是做偏后端技術(shù),如處理一些消息,執(zhí)行一些計(jì)算任務(wù),也經(jīng)常需要用到線程池技術(shù)。
鑒于線程池技術(shù)的重要性,接下來(lái)會(huì)分多篇介紹java中提供的ThreadPoolExecutor線程池實(shí)現(xiàn)的底層機(jī)制。
只有對(duì)機(jī)制了然于胸,才能更好駕馭這把利器。
線程池技術(shù)演示流程
- 關(guān)鍵概念: 如果說(shuō)怎么最容易了解線程池的實(shí)現(xiàn)原理,那就是一步一步動(dòng)態(tài)的演示。為了便于理解,這里先介紹幾個(gè)線程池用到的概念。
- ThreadPoolExecutor: 這是線程池實(shí)現(xiàn)類(lèi),會(huì)動(dòng)態(tài)創(chuàng)建多個(gè)線程,并發(fā)執(zhí)行提交的多個(gè)任務(wù);
- Worker: 是個(gè)Runnable實(shí)現(xiàn)類(lèi)來(lái)的,內(nèi)部會(huì)創(chuàng)建一個(gè)線程,一直循環(huán)不斷執(zhí)行任務(wù),所以可以認(rèn)為一個(gè)Worker就是一個(gè)工作線程;
- corePoolSize: 當(dāng)池中總線程數(shù)<corePoolSize時(shí),提交一個(gè)任務(wù)創(chuàng)建一個(gè)新線程,而不管已經(jīng)存在的線程是不是閑著,通常情況下,一旦線程數(shù)達(dá) 到了corePoolSize,那么池中總線程數(shù)是不會(huì)跌破到corePoolSize以下的。(除非 allowCoreThreadTimeOut=true并且keepAliveTime>0);
- maximumPoolSize: 當(dāng)池中線程數(shù)達(dá)到了corePoolSize,這時(shí)候新提交的任務(wù)就會(huì)放入等待隊(duì)列中,一般情況下,這些任務(wù)會(huì)被前面創(chuàng)建的 corePoolSize個(gè)線程執(zhí)行。當(dāng)任務(wù)提交速度過(guò)快,隊(duì)列滿了,這時(shí)候,如果當(dāng)前總線程數(shù)<maximumPoolSize,那么線程池會(huì)創(chuàng)建一個(gè)新的線程來(lái)執(zhí)行新提交的任務(wù),否則根據(jù)策略放棄任務(wù);
- keepAliveTime:存活時(shí)間,分兩種情況: (1)allowCoreThreadTimeOut=true,所有線程,一旦創(chuàng)建后,在keepAliveTime時(shí)間內(nèi),如果沒(méi)有任務(wù)可以執(zhí)行,則該線程會(huì)退出并銷(xiāo)毀,這樣的好處是系統(tǒng)不忙時(shí)可以回收線程資源;(2)allowCoreThreadTimeOut=false,如果總線程數(shù)<=corePoolSize,那么這些線程是不會(huì)退出的,他們會(huì)一直不斷的等待任務(wù)并執(zhí)行,哪怕當(dāng)前沒(méi)有任務(wù),但如果線程數(shù)>corePoolSize,而且一旦一個(gè)線程閑的時(shí)間超過(guò) keepAliveTime則會(huì)退出,但一旦降低到corePoolSize,則不會(huì)再退出了。
- allowCoreThreadTimeOut: 用于決定是否在系統(tǒng)閑時(shí)可以逐步回收所有的線程,如果為allowCoreThreadTimeOut=true,必須結(jié)合keepAliveTime一起使用,用于決定當(dāng)線程數(shù)<corePoolSize時(shí),是否要回收這些線程。
- workQueue:這是一個(gè)阻塞隊(duì)列,當(dāng)線程數(shù)>=corePoolSize,這時(shí)候提交的任務(wù)將會(huì)放入阻塞隊(duì)列中,如果阻塞隊(duì)列是無(wú)界的,那么總的線程數(shù)是不可能>corePoolSize的,即maximumPoolSize屬性就是無(wú)用的;如果阻塞隊(duì)列是有界的,而且未滿,則任務(wù)入隊(duì),否則根據(jù)maximumPoolSize的值判斷是要新建線程執(zhí)行新任務(wù)或者是根據(jù)策略丟棄任務(wù)。
有了以上的概念,接下來(lái)將根據(jù) allowCoreThreadTimeOut的值分兩種場(chǎng)景進(jìn)行演示說(shuō)明。
演示一: allowCoreThreadTimeOut=true
1、 初值設(shè)定: corePoolSize=2; maximumPoolSize=3;keepAliveTime=10s; workQueue容量為2; 初始狀態(tài)圖如下所示:
2、submit一個(gè)任務(wù)A,由于總線程數(shù)0<corePoolSize; 此時(shí)會(huì)創(chuàng)建一個(gè)線程執(zhí)行任務(wù)A,狀態(tài)圖如下:
3、submit一個(gè)任務(wù)B,由于總線程數(shù)1<corePoolSize,此時(shí)會(huì)創(chuàng)建一個(gè)線程執(zhí)行任務(wù)B,狀態(tài)圖如下:
4、submit一個(gè)任務(wù)C,由于總線程數(shù) 2=corePoolSize,workQueue不滿,這時(shí)候任務(wù)C入隊(duì)列,狀態(tài)圖如下:
5、submit一個(gè)任務(wù)D,很明顯,任務(wù)D入隊(duì)列,狀態(tài)圖如下:
6、submit一個(gè)任務(wù)E,這時(shí)候,線程數(shù)2=corePoolSize,workQueue也已經(jīng)滿了,判斷發(fā)現(xiàn)線程數(shù)2<maximumPoolSize,所以繼續(xù)創(chuàng)建線程執(zhí)行任務(wù)E,狀態(tài)圖如下:
7、submit一個(gè)任務(wù)F,這時(shí)候,2=corePoolSize,workQueue已滿,判斷發(fā)現(xiàn)線程數(shù)3=maximumPoolSize,這種情況下,線程池會(huì)根據(jù)策略來(lái)決定是否要放棄當(dāng)前任務(wù),或者是把workQueue中一個(gè)任務(wù)刪除,然后入隊(duì)新的任務(wù),也可以自定義策略,比如,持久化到DB之類(lèi)的,或者是發(fā)出警報(bào)。我們假設(shè)是直接丟棄策略,這時(shí)候狀態(tài)圖不變。
8、這會(huì)沒(méi)有新任務(wù)到來(lái)了,各個(gè)任務(wù)陸續(xù)執(zhí)行完了,包括隊(duì)列中的C和D也執(zhí)行完了,這時(shí)候,由于當(dāng)前場(chǎng)景為allowCoreThreadTimeOut=true,如果在等待keepAliveTime時(shí)間后線程仍舊無(wú)法獲取新的任務(wù),線程將會(huì)自行退出,這將導(dǎo)致最終所有線程都退出了,也就是又再次回到了原始狀態(tài),如下圖所示:
說(shuō)得更簡(jiǎn)單一些就是:在 allowCoreThreadTimeOut=true時(shí),如果一個(gè)線程等了keepAliveTime還無(wú)法獲取新任務(wù),則退出。
演示二:allowCoreThreadTimeOut=false
1~7 的步驟與狀態(tài)跟“演示一”是一樣的,所以這里不再贅述,這時(shí)候狀態(tài)圖如下:
8、這會(huì)沒(méi)有新任務(wù)到來(lái)了,各個(gè)任務(wù)陸續(xù)執(zhí)行完了,包括隊(duì)列中的C和D也執(zhí)行完了,這時(shí)候,由于當(dāng)前場(chǎng)景為allowCoreThreadTimeOut=false,并且線程數(shù)3>corePoolSize,這時(shí)候每個(gè)線程都感知到線程數(shù)過(guò)多,所以它們都會(huì)嘗試把自己停止掉,實(shí)現(xiàn)中最終只會(huì)停止一個(gè)線程,剩余線程數(shù)2=corePoolSize,接下來(lái),哪怕一直沒(méi)有新任務(wù)來(lái),這corePoolSize個(gè)線程也不會(huì)退出,一直存活著等待接收任務(wù)。這時(shí)候,狀態(tài)圖如下:
線程池的狀態(tài)
前一部分演示了線程池的基本實(shí)現(xiàn)原理,這一小節(jié)介紹一下線程池的狀態(tài),線程池概括上講有5種狀態(tài),如下圖所示:
- RUNNING狀態(tài): 創(chuàng)建線程池的時(shí)候,線程池的初始狀態(tài)為 RUNNING,接著就可以提交任務(wù)執(zhí)行了。
- SHUTDOWN狀態(tài): 當(dāng)在RUNNING狀態(tài)調(diào)用shutdown()時(shí),線程池狀態(tài)會(huì)被改為SHUTDOWN,這時(shí)候,submit任務(wù)的時(shí)候,會(huì)被拒絕,可以使用多種拒絕策略, 比如最簡(jiǎn)單就是直接丟棄任務(wù)。至于正在執(zhí)行中的線程,會(huì)繼續(xù)執(zhí)行,同時(shí)會(huì)把阻塞隊(duì)列中的任務(wù)也一并執(zhí)行完畢,等到全部任務(wù)執(zhí)行完畢,線程池會(huì)進(jìn)入 TIDYING狀態(tài),等執(zhí)行鉤子方法terminated()之后,就會(huì)進(jìn)入最終狀態(tài)TERMINATED,這時(shí)候,整個(gè)線程池完全終止。
- STOP狀態(tài): 當(dāng)在RUNNING狀態(tài)調(diào)用shutdownNow()時(shí),線程池狀態(tài)會(huì)被改為STOP,這時(shí)候,submit任務(wù)會(huì)被拒絕,那么如果有任務(wù)執(zhí)行到一半,該怎么處理?其實(shí),執(zhí)行shutdownNow()時(shí),會(huì)中斷各個(gè)工作線程,所以任務(wù)會(huì)如何執(zhí)行要看任務(wù)做的是什么事情,有沒(méi)有處理中斷異常。而阻塞隊(duì)列如何有任務(wù),這些任務(wù)將不會(huì)再執(zhí)行,shutdownNow()執(zhí)行后,將會(huì)返回阻塞隊(duì)列中的未執(zhí)行的任務(wù)列表。
- TIDYING狀態(tài): TIDYING只是一個(gè)過(guò)渡狀態(tài),當(dāng)所有工作線程都停止后,線程池的狀態(tài)會(huì)進(jìn)入TIDYING,然后執(zhí)行一個(gè)鉤子方法terminated(),最后線程池會(huì)進(jìn)入TERMINATED狀態(tài)。
- TERMINATED狀態(tài): 線程池終止?fàn)顟B(tài),這個(gè)沒(méi)什么可說(shuō)的了,大家都明白。
總結(jié)
理解線程池最主要是要理解線程池幾個(gè)主要的配置參數(shù),如果不看實(shí)現(xiàn)細(xì)節(jié),原理還是比較簡(jiǎn)單的。在了解原理的基礎(chǔ)上再去看代碼,就會(huì)事半功倍。
到此這篇關(guān)于java線程池ThreadPoolExecutor實(shí)現(xiàn)原理詳解的文章就介紹到這了,更多相關(guān)ThreadPoolExecutor實(shí)現(xiàn)原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Sentinel鏈路模式規(guī)則無(wú)效問(wèn)題
本文介紹了如何在Spring Cloud Alibaba項(xiàng)目中使用Sentinel鏈路流控規(guī)則,并解決規(guī)則不生效的問(wèn)題,通過(guò)關(guān)閉Sentinel過(guò)濾器,可以避免重復(fù)統(tǒng)計(jì)請(qǐng)求2025-01-01java實(shí)現(xiàn)即賦值也判斷的寫(xiě)法示例
這篇文章主要為大家介紹了java實(shí)現(xiàn)即賦值也判斷的寫(xiě)法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12詳解Java8新特性之interface中的static方法和default方法
這篇文章主要介紹了Java8新特性之interface中的static方法和default方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08Java如何接收前端easyui?datagrid傳遞的數(shù)組參數(shù)
這篇文章分享一下怎么在easyui的datagrid刷新表格時(shí),在后端java代碼中接收datagrid傳遞的數(shù)組參數(shù),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-11-11Java基本數(shù)據(jù)類(lèi)型與對(duì)應(yīng)的包裝類(lèi)(動(dòng)力節(jié)點(diǎn)java學(xué)院整理)
Java是面向?qū)ο蟮木幊陶Z(yǔ)言,包裝類(lèi)的出現(xiàn)更好的體現(xiàn)這一思想,Java語(yǔ)言提供了八種基本類(lèi)型。六種數(shù)字類(lèi)型(四個(gè)整數(shù)型,兩個(gè)浮點(diǎn)型),一種字符類(lèi)型,還有一種布爾型。 下面通過(guò)本文給大家詳細(xì)介紹,感興趣的朋友一起學(xué)習(xí)吧2017-04-04SpringBoot框架DataSource多數(shù)據(jù)源配置方式
這篇文章主要介紹了SpringBoot框架DataSource多數(shù)據(jù)源配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07