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