執行緒池實現「執行緒復用」的原理?

2021-10-18 22:54:24 字數 3009 閱讀 9946

執行緒復用原理

我們知道執行緒池會使用固定數量或可變數量的執行緒來執行任務,但無論是固定數量或可變數量的執行緒,其執行緒數量都遠遠小於任務數量,面對這種情況執行緒池可以通過執行緒復用讓同乙個執行緒去執行不同的任務,那麼執行緒復用背後的原理是什麼呢?

執行緒池建立新執行緒的時機和規則:

如流程圖所示,當提交任務後,執行緒池首先會檢查當前執行緒數,如果此時執行緒數小於核心執行緒數,比如最開始執行緒數量為 0,則新建執行緒並執行任務,隨著任務的不斷增加,執行緒數會逐漸增加並達到核心執行緒數,此時如果仍有任務被不斷提交,就會被放入 workqueue 任務佇列中,等待核心執行緒執行完當前任務後重新從 workqueue 中提取正在等待被執行的任務。此時,假設我們的任務特別的多,已經達到了 workqueue 的容量上限,這時執行緒池就會啟動後備力量,也就是 maxpoolsize 最大執行緒數,執行緒池會在 corepoolsize 核心執行緒數的基礎上繼續建立執行緒來執行任務,假設任務被不斷提交,執行緒池會持續建立執行緒直到執行緒數達到 maxpoolsize 最大執行緒數,如果依然有任務被提交,這就超過了執行緒池的最大處理能力,這個時候執行緒池就會拒絕這些任務,我們可以看到實際上任務進來之後,執行緒池會逐一判斷 corepoolsize 、workqueue 、maxpoolsize ,如果依然不能滿足需求,則會拒絕任務。

我們接下來具體看看**是如何實現的,我們從 execute 方法開始分析,原始碼如下所示。

public void execute(runnable command)  

if (isrunning(c) && workqueue.offer(command))  

else if (!addworker(command, false)) 

reject(command);

}

執行緒復用原始碼解析

這段**短小精悍,內容豐富,接下來我們具體分析**中的邏輯,首先看下前幾行:

//如果傳入的runnable的空,就丟擲異常

if (command == null) 

throw new nullpointerexception();

execute 方法中通過 if 語句判斷 command ,也就是 runnable 任務是否等於 null,如果為 null 就丟擲異常。

接下來判斷當前執行緒數是否小於核心執行緒數,如果小於核心執行緒數就呼叫 addworker() 方法增加乙個 worker,這裡的 worker 就可以理解為乙個執行緒:

if (workercountof(c)

if (isrunning(c) && workqueue.offer(command))
如果**執行到這裡,說明當前執行緒數大於或等於核心執行緒數或者 addworker 失敗了,那麼就需要通過if (isrunning(c) && workqueue.offer(command))檢查執行緒池狀態是否為 running,如果執行緒池狀態是 running 就把任務放入任務佇列中,也就是 workqueue.offer(command)。如果執行緒池已經不處於 running 狀態,說明執行緒池被關閉,那麼就移除剛剛新增到任務佇列中的任務,並執行拒絕策略,**如下所示:

if (! isrunning(recheck) && remove(command)) 

reject(command);

下面我們再來看後乙個 else 分支:

else if (workercountof(recheck) == 0) 

addworker(null, false);

能進入這個 else 說明前面判斷到執行緒池狀態為 running,那麼當任務被新增進來之後就需要防止沒有可執行執行緒的情況發生(比如之前的執行緒被**了或意外終止了),所以此時如果檢查當前執行緒數為 0,也就是 workercountof(recheck) == 0,那就執行 addworker() 方法新建執行緒。

我們再來看最後一部分**:

else if (!addworker(command, false)) 

reject(command);

執行到這裡,說明執行緒池不是 running 狀態或執行緒數大於或等於核心執行緒數並且任務佇列已經滿了,根據規則,此時需要新增新執行緒,直到執行緒數達到「最大執行緒數」,所以此時就會再次呼叫 addworker 方法並將第二個引數傳入 false,傳入 false 代表增加執行緒時判斷當前執行緒數是否少於 maxpoolsize,小於則增加新執行緒,大於等於則不增加,也就是以 maxpoolsize 為上限建立新的 worker;addworker 方法如果返回 true 代表新增成功,如果返回 false 代表任務新增失敗,說明當前執行緒數已經達到 maxpoolsize,然後執行拒絕策略 reject 方法。如果執行到這裡執行緒池的狀態不是 running,那麼 addworker 會失敗並返回 false,所以也會執行拒絕策略 reject 方法。

可以看出,在 execute 方法中,多次呼叫 addworker 方法把任務傳入,addworker 方法會新增並啟動乙個 worker,這裡的 worker 可以理解為是對 thread 的包裝,worker 內部有乙個 thread 物件,它正是最終真正執行任務的執行緒,所以乙個 worker 就對應執行緒池中的乙個執行緒,addworker 就代表增加執行緒。執行緒復用的邏輯實現主要在 worker 類中的 run 方法裡執行的 runworker 方法中,簡化後的 runworker 方法**如下所示。

runworker(worker w)  finally }}

可以看出,實現執行緒復用的邏輯主要在乙個不停迴圈的 while 迴圈體中。

在這裡,我們找到了最終的實現,通過取 worker 的 firsttask 或者 gettask方法從 workqueue 中取出了新任務,並直接呼叫 runnable 的 run 方法來執行任務,也就是如之前所說的,每個執行緒都始終在乙個大迴圈中,反覆獲取任務,然後執行任務,從而實現了執行緒的復用。

執行緒池執行緒復用原理(原始碼詳解)

假設有50個任務,執行緒池設定核心執行緒數為3,等待佇列數設定為5,那麼執行這50個任務時,這3個核心執行緒和2個非核心執行緒就會不停的復用,進行任務的執行。2.1執行緒池的工作流程 當任務提交之後,執行緒池首先會檢查當前執行緒數,如果當前的執行緒數小於核心執行緒數 corepoolsize 則新建...

執行緒池實現原理

上面這幅圖作者表達的不夠完整,作者想通過如下文本來表達內含本質。過程如下 如果請求執行緒小於執行緒池目標執行緒,則執行緒池會新建立執行緒來處理請求 如果請求執行緒數過多,超過了目標執行緒則將請求任務放入佇列中進行緩衝 如果佇列滿了 但未達到最大執行緒池數,這時會新建立執行緒 直到上限為止即maxpo...

執行緒池實現原理

蘑菇街面試,設計乙個執行緒池 入隊非阻塞佇列 當佇列中滿了時候,放入資料,資料丟失 阻塞佇列 當佇列滿了的時候,進行等待,什麼時候佇列中有出隊的資料,那麼第11個再放進去 出隊非阻塞佇列 如果現在佇列中沒有元素,取元素,得到的是null 阻塞佇列 等待,什麼時候放進去,再取出來 執行緒池使用的是阻塞...