通過Thread Pool Executor類解析線程池執行任務的核心流程

摘要:ThreadPoolExecutor是Java線程池中最核心的類之一,它能夠保證線程池按照正常的業務邏輯執行任務,并通過原子方式更新線程池每個階段的狀態 。
本文分享自華為云社區《【高并發】通過Thread Pool Executor類的源碼深度解析線程池執行任務的核心流程》,作者:冰 河 。
今天,我們通過Thread Pool Executor類的源碼深度解析線程池執行任務的核心流程,小伙伴們最好是打開IDEA,按照步驟 , 調試下Thread Pool Executor類的源碼,這樣會理解的更加深刻,好了,開始今天的主題 。
核心邏輯概述Thread PoolExecutor是Java線程池中最核心的類之一,它能夠保證線程池按照正常的業務邏輯執行任務,并通過原子方式更新線程池每個階段的狀態 。
ThreadPoolExecutor類中存在一個workers工作線程集合,用戶可以向線程池中添加需要執行的任務,workers集合中的工作線程可以直接執行任務,或者從任務隊列中獲取任務后執行 。ThreadPoolExecutor類中提供了整個線程池從創建到執行任務,再到消亡的整個流程方法 。本文,就結合ThreadPoolExecutor類的源碼深度分析線程池執行任務的整體流程 。
在ThreadPoolExecutor類中,線程池的邏輯主要體現在execute(Runnable)方法,addWorker(Runnable, boolean)方法,addWorkerFailed(Worker)方法和拒絕策略上 , 接下來,我們就深入分析這幾個核心方法 。
execute(Runnable)方法execute(Runnable)方法的作用是提交Runnable類型的任務到線程池中 。我們先看下execute(Runnable)方法的源碼,如下所示 。
public void execute(Runnable command) {//如果提交的任務為空 , 則拋出空指針異常if (command == null)throw new NullPointerException();//獲取線程池的狀態和線程池中線程的數量int c = ctl.get();//線程池中的線程數量小于corePoolSize的值if (workerCountOf(c) < corePoolSize) {//重新開啟線程執行任務if (addWorker(command, true))return;c = ctl.get();}//如果線程池處于RUNNING狀態,則將任務添加到阻塞隊列中if (isRunning(c) && workQueue.offer(command)) {//再次獲取線程池的狀態和線程池中線程的數量,用于二次檢查int recheck = ctl.get();//如果線程池沒有未處于RUNNING狀態 , 從隊列中刪除任務if (! isRunning(recheck) && remove(command))//執行拒絕策略reject(command);//如果線程池為空,則向線程池中添加一個線程else if (workerCountOf(recheck) == 0)addWorker(null, false);}//任務隊列已滿,則新增worker線程,如果新增線程失敗,則執行拒絕策略else if (!addWorker(command, false))reject(command);}整個任務的執行流程,我們可以簡化成下圖所示 。
通過Thread Pool Executor類解析線程池執行任務的核心流程

文章插圖
接下來 , 我們拆解execute(Runnable)方法,具體分析execute(Runnable)方法的執行邏輯 。
(1)線程池中的線程數是否小于corePoolSize核心線程數,如果小于corePoolSize核心線程數,則向workers工作線程集合中添加一個核心線程執行任務 。代碼如下所示 。
//線程池中的線程數量小于corePoolSize的值if (workerCountOf(c) < corePoolSize) {//重新開啟線程執行任務if (addWorker(command, true))return;c = ctl.get();}(2)如果線程池中的線程數量大于corePoolSize核心線程數,則判斷當前線程池是否處于RUNNING狀態,如果處于RUNNING狀態,則添加任務到待執行的任務隊列中 。注意:這里向任務隊列添加任務時,需要判斷線程池是否處于RUNNING狀態,只有線程池處于RUNNING狀態時,才能向任務隊列添加新任務 。否則,會執行拒絕策略 。代碼如下所示 。
【通過Thread Pool Executor類解析線程池執行任務的核心流程】if (isRunning(c) && workQueue.offer(command)) (3)向任務隊列中添加任務成功,由于其他線程可能會修改線程池的狀態,所以這里需要對線程池進行二次檢查 , 如果當前線程池的狀態不再是RUNNING狀態 , 則需要將添加的任務從任務隊列中移除,執行后續的拒絕策略 。如果當前線程池仍然處于RUNNING狀態 , 則判斷線程池是否為空,如果線程池中不存在任何線程,則新建一個線程添加到線程池中,如下所示 。
//再次獲取線程池的狀態和線程池中線程的數量,用于二次檢查int recheck = ctl.get();//如果線程池沒有未處于RUNNING狀態,從隊列中刪除任務if (! isRunning(recheck) && remove(command))//執行拒絕策略reject(command);//如果線程池為空,則向線程池中添加一個線程else if (workerCountOf(recheck) == 0)addWorker(null, false);(4)如果在步驟(3)中向任務隊列中添加任務失敗 , 則嘗試開啟新的線程執行任務 。此時,如果線程池中的線程數量已經大于線程池中的最大線程數maximumPoolSize,則不能再啟動新線程 。此時,表示線程池中的任務隊列已滿,并且線程池中的線程已滿,需要執行拒絕策略,代碼如下所示 。

推薦閱讀