1. 程式人生 > >線程池的工作原理與源碼解讀

線程池的工作原理與源碼解讀

分享 指定 blog 系統資源 時也 就會 起名字 服務端 面試

隨著cpu核數越來越多,不可避免的利用多線程技術以充分利用其計算能力。所以,多線程技術是服務端開發人員必須掌握的技術。 線程的創建和銷毀,都涉及到系統調用,比較消耗系統資源,所以就引入了線程池技術,避免頻繁的線程創建和銷毀。 在Java用有一個Executors工具類,可以為我們創建一個線程池,其本質就是new了一個ThreadPoolExecutor對象。線程池幾乎也是面試必考問題。本節結合源代碼,說說ThreadExecutor的工作原理 一、線程池創建 先看一下ThreadPoolExecutor參數最全的構造方法: 技術分享圖片 技術分享圖片 ①corePoolSize:線程池的核心線程數,說白了就是,即便是線程池裏沒有任何任務,也會有corePoolSize個線程在候著等任務。 ②maximumPoolSize:最大線程數,不管你提交多少任務,線程池裏最多工作線程數就是maximumPoolSize。 ③keepAliveTime:線程的存活時間。當線程池裏的線程數大於corePoolSize時,如果等了keepAliveTime時長還沒有任務可執行,則線程退出。 ⑤unit:這個用來指定keepAliveTime的單位,比如秒:TimeUnit.SECONDS。 ⑥workQueue:一個阻塞隊列,提交的任務將會被放到這個隊列裏。 ⑦threadFactory:線程工廠,用來創建線程,主要是為了給線程起名字,默認工廠的線程名字:pool-1-thread-3。 ⑧handler:拒絕策略,當線程池裏線程被耗盡,且隊列也滿了的時候會調用。 以上就是創建線程池時用到的參數,面試中經常會有面試官問道這個問題。 二、線程池執行流程
這裏用一個圖來說明線程池的執行流程 技術分享圖片 技術分享圖片 任務被提交到線程池,會先判斷當前線程數量是否小於corePoolSize,如果小於則創建線程來執行提交的任務,否則將任務放入workQueue隊列,如果workQueue滿了,則判斷當前線程數量是否小於maximumPoolSize,如果小於則創建線程執行任務,否則就會調用handler,以表示線程池拒絕接收任務。 這裏以jdk1.8.0_111的源代碼為例,看一下具體實現。 1、先看一下線程池的executor方法 技術分享圖片 技術分享圖片 ①:判斷當前活躍線程數是否小於corePoolSize,如果小於,則調用addWorker創建線程執行任務 ②:如果不小於corePoolSize,則將任務添加到workQueue隊列。 ③:如果放入workQueue失敗,則創建線程執行任務,如果這時創建線程失敗(當前線程數不小於maximumPoolSize時),就會調用reject(內部調用handler)拒絕接受任務。 2、再看下addWorker的方法實現
技術分享圖片 技術分享圖片 這塊代碼是在創建非核心線程時,即core等於false。判斷當前線程數是否大於等於maximumPoolSize,如果大於等於則返回false,即上邊說到的③中創建線程失敗的情況。 addWorker方法的下半部分: 技術分享圖片 技術分享圖片 ①創建Worker對象,同時也會實例化一個Thread對象。 ②啟動啟動這個線程 3、再到Worker裏看看其實現 技術分享圖片 技術分享圖片 可以看到在創建Worker時會調用threadFactory來創建一個線程。上邊的②中啟動一個線程就會觸發Worker的run方法被線程調用。 4、接下來咱們看看runWorker方法的邏輯 技術分享圖片 技術分享圖片 線程調用runWoker,會while循環調用getTask方法從workerQueue裏讀取任務,然後執行任務。只要getTask方法不返回null,此線程就不會退出。 5、最後在看看getTask方法實現
技術分享圖片 技術分享圖片 ①咱們先不管allowCoreThreadTimeOut,這個變量默認值是false。wc>corePoolSize則是判斷當前線程數是否大於corePoolSize。 ②如果當前線程數大於corePoolSize,則會調用workQueue的poll方法獲取任務,超時時間是keepAliveTime。如果超過keepAliveTime時長,poll返回了null,上邊提到的while循序就會退出,線程也就執行完了。 如果當前線程數小於corePoolSize,則會調用workQueue的take方法阻塞在當前。

線程池的工作原理與源碼解讀