原始碼學習 執行緒池原始碼自學篇

2021-10-22 15:38:56 字數 3158 閱讀 1169

執行緒池作為專案中經常用到的類,也在面試中備受青睞,個人對於原始碼也只是讀過一些,很多知識點都是一知半解,藉此機會自己再溫故一下。我是在邊自學邊寫這些東西,可能語言上或者邏輯上不太完善,請大家不要介意。

執行緒池是什麼?

官網解釋:

執行緒池主要解決兩個問題

在需要執行大量執行緒的場景,減少建立每個執行緒帶來的開銷,從而提公升軟體效能

它提供了一些方法來約束和管理執行任務集合時消耗的資源(包括執行緒)

我們先看一下這個threadpoolexcutor的類圖,簡單有乙個了解

(後記,梳理乙個類圖能把人搞絕望,真複雜啊,原始碼也真心難啃,建議大家一點點來,別心急,心急也看不懂)

開始具體分析

如何減少的執行緒開銷呢?應該就是執行緒池中復用執行緒的邏輯吧,

我們順著threadpoolexecutor.execute方法來一步步探索吧。

execute方法體內部比較人性化的一點是,先通過注釋給大家闡述一下這裡的執行邏輯,簡單翻譯一下

如果當前執行的執行緒比設定的核心執行緒數要少,則開啟乙個新的執行緒,並將傳入的runnable作為它的第乙個任務,呼叫addworker會自動檢查執行狀態和執行數量,防止誤報的情況

如果乙個任務順利加入佇列中,我們仍然需要再次檢查是否有必要新增乙個新的執行緒(因為有可能在上次檢查以後有新的執行緒停掉)或者執行緒池停掉。因此我們需要再次檢查執行狀態,如果有必要,我們需要回退新增操作,或者開啟乙個新執行緒

如果我們不能將任務新增至佇列中,我們嘗試新增乙個新的執行緒,如果這裡也操作失敗,應該是執行緒池已經關閉或者飽和,因此拒絕了這個任務

如果當前執行執行緒數如果當前執行執行緒數》=corepoolsize,則將任務新增至任務佇列

如果當前任務佇列滿了,並且當前任務數

首先看一下第一步,如何建立新的thread,我們繼續按照流程圖的方式來展示,我一直覺得圖比**要更有表現力。

這裡我們要細究一下上乙個流程圖中開啟任務成功部分的**

private

boolean

addworker

(runnable firsttask,

boolean core)

}finally

if(workeradded)}}

finally

return workerstarted;

}

**1處建立worker物件的邏輯如下

private

final

class

worker

extends

abstractqueuedsynchronizer

implements

runnable

/** delegates main run loop to outer runworker. */

public

void

run(

) ……

}

這裡worker本身實現了runnable介面,並在newthread的時候將自身傳進去,那麼其實在上一段**2處的t.start執行的是worker的run方法。接下來我們仔細看一下runworker方法這裡到底有什麼巧妙的邏輯。

沒辦法貼一下**吧

final

void

runworker

(worker w)

finally

} completedabruptly =

false;}

finally

}

上面**中最重要的一點就是while裡面的條件,如果當前worker的task不為空或者從佇列中獲取不為空,則就不銷毀。這裡大家肯定會疑惑,如果佇列中沒有任務,那麼gettask肯定為空,執行緒不就銷毀了嗎?所以我們從gettask這個方法中查詢我們想要知道的答案.。

/**

*通過阻塞或者定時等待得到乙個task,當然這取決於當前的配置設定,或者在以下幾種場景中worker必須退出,因而返回* *null。

*1. wokers數量大於等於maxpoolsize

*2.執行緒池已停止

*3.執行緒池關閉並且隊列為空

*4. 等待task超時,超時的woker注定要被曉慧

***/

private runnable gettask()

int wc =

workercountof

(c);

// woker是否會被銷毀?如果設定了核心執行緒的超時時間或者當前worker的數量超過了核心執行緒數

boolean timed = allowcorethreadtimeout || wc > corepoolsize;if(

(wc > maximumpoolsize ||

(timed && timedout))&&

(wc >

1|| workqueue.

isempty()

))trycatch

(interruptedexception retry)

}}

可以看到,這裡是利用了阻塞佇列的特性,可以等待指定時間或者一直阻塞。阻塞佇列可以用來解決生產者-消費者場景中資料新增和消費的邏輯,阻塞佇列是執行緒安全的,阻塞佇列內部一般是通過reentrantlock來保證執行緒安全的,關於阻塞佇列這塊後續可以單獨寫一篇,我覺得自己現在也沒有搞懂。

關於這一點,可以通過檢視上面的類圖得知,執行緒池有很多屬性都可以設定,有很多方法可以停止或者打斷執行緒池。這裡就沒啥好說的了。

執行緒池的拒絕策略也經常被問起,從前方類圖可以看到threadpoolexecutor有四個內部類分別由自己策略

類名邏輯

callerrunspolicy

如果執行緒池沒有關閉的情況下,在runnable自身執行緒執行run方法

abortpolicy(預設)

丟擲異常

discardpolicy

什麼也不做,達到discard的目的

discardoldestpolicy

移除等待佇列head的任務,將當前待新增任務新增進去

mysql 執行緒池原始碼 執行緒池原始碼解析

1.前言 我個人覺得理論性的東西可能大家都懂,但是具體的實現細節可能並不是很清楚所以才想記錄一下,加深記憶。2.關鍵原始碼解析 1 ctl private final atomicinteger ctl new atomicinteger ctlof running,0 private static...

Cartographer原始碼篇 原始碼分析 1

在安裝編譯cartographer 1.0.0的時候,我們可以看到 主要包括cartorgarpher ros cartographer ceres sover三個部分。其中,ceres solver用於非線性優化,求解最小二乘問題 cartographer ros為ros平台的封裝,獲取感測器資料...

執行緒池原始碼閱讀(二)

僅大致過了下,有問題的請指出,謝謝。1.8通過乙個樣本場景了解新增任務流程。執行緒池配置 任務 輸出1,睡眠300s,輸出2 執行 新增9個任務至執行緒池 submit 提交任務使用submit 方法,如下 關鍵方法execute public future submit runnable task...