執行緒池作為專案中經常用到的類,也在面試中備受青睞,個人對於原始碼也只是讀過一些,很多知識點都是一知半解,藉此機會自己再溫故一下。我是在邊自學邊寫這些東西,可能語言上或者邏輯上不太完善,請大家不要介意。執行緒池是什麼?
官網解釋:
執行緒池主要解決兩個問題我們先看一下這個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...