核心池:
線程池內線程數量小于等于 coreSize 的部分我稱為核心池,核心池是線程池的常駐部分,內部的線程一般不會被銷毀,我們提交的任務也應該絕大部分都由核心池內的線程來執行。
線程創建時機的誤解:
有關核心池最常見的一個誤區是沒搞清楚核心池內線程的創建時機,這個問題,我覺得甩 10% 的鍋給 Doug Lea 大神應該不算過分,因為他在文檔里寫道 “If fewer than corePoolSize threads are running, try to start a new thread with the given command as its first task”,其中 "running" 這個詞就比較有歧義,因為在我們理解里 running 是指當前線程已被操作系統調度,擁有操作系統時間分片,或者被理解為正在執行某個任務。
基于以上的理解,我們很容易就認為如果任務的 QPS 非常低,線程池內線程數量永遠也達不到 coreSize。即如果我們配置了 coreSize 為 1000,實際上 QPS 只有 1,單個任務耗時 1s,那么核心池大小就會一直是 1,即使有流量抖動,核心池也只會被擴容到 3。因為一個線程每秒執行執行一個任務,剛好不用創建新線程就足以應對 1QPS。
創建過程:
但如果簡單設計一個測試,使用 jstack 打印出線程棧并數一下線程池內線程數量,會發現線程池內的線程數會隨著任務的提交而逐漸增大,直到達到 coreSize。
因為核心池的設計初衷是想它能作為常駐池,承載日常流量,所以它應該被盡快初始化,于是線程池的邏輯是在沒有達到 coreSize 之前,每一個任務都會創建一個新的線程。
基于此,我們對一些高并發服務進行的預熱,其實并不是期望 JVM 能對熱點代碼做 JIT 等優化,對線程池、連接池和本地緩存的預熱才是重點。
BlockingQueue 是線程池內的另一個重要組件,首先它是線程池” 生產者 - 消費者” 模型的中間媒介,另外它也可以為大量突發的流量做緩沖,但理解和配置它也經常會出錯。
運行模型:
最常見的錯誤是不理解線程池的運行模型。首先要明確的一點是線程池并沒有準確的調度功能,即它無法感知有哪些線程是處于空閑狀態的,并把提交的任務派發給空閑線程。線程池采用的是” 生產者 - 消費者” 模式,除了觸發線程創建的任務(線程的 firstTask)不會入 BlockingQueue 外,其他任務都要進入到 BlockingQueue,等待線程池內的線程消費,而任務會被哪個線程消費到完全取決于操作系統的調度。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.