1. 程式人生 > >java執行緒池學習(六) —— 執行緒池的合理配置

java執行緒池學習(六) —— 執行緒池的合理配置

一、確定執行緒數

在工作中,為了加快程式的處理速度,我們需要將問題分解成若干個併發執行的任務。接著我們將這些任務委派給執行緒,以便使它們可以併發的執行。但是需要注意的是,由於資源所限,我們不能建立過多的執行緒。

這就涉及到一個 確定建立多少執行緒數才是合理 的問題。 

《java虛擬機器併發程式設計》一書中,對這個問題有詳盡的解答,本人在此摘取歸納如下:

1.我們可以先獲取到系統可用的處理器核心數:

Runtime.getRuntime().availableProcessors()

2.確定任務的型別:

如果所有任務都是計算密集型的,則建立處理器可用核心數那麼多的執行緒數就可以了。

在這種情況下,建立更多的執行緒對程式的效能而言反而是不利的。因為當有多個任務處於就緒狀態時,處理器核心需要線上程間頻繁進行上下文切換

,而這種切換對程式效能損耗較大。

如果任務都是IO密集型的,那麼我們需要開更多的執行緒來提高效能。

當一個任務執行IO操作時,其執行緒被阻塞,於是處理器可以立即進行上下文切換以便處理其他就緒執行緒。如果我們只有處理器可用核心數那麼多執行緒的話,則即使有待執行的任務也無法處理,因為我們已經拿不出更多的執行緒供處理器排程了。

3.計算出程式所需的執行緒數:

首先我們要明白一個概念叫 阻塞係數

如果任務有50%的時間處於阻塞狀態,則阻塞係數為0.5。則程式所需的執行緒數為處理器可用核心數的兩倍。如果任務被阻塞的時間少於50%,即這些任務是計算密集型的,則程式所需執行緒數將隨之減少,但最少也不應該低於處理器的核心數。如果任務被阻塞的時間大於執行時間,即該任務是IO密集型的,我們就需要建立比處理器核心數大幾倍數量的執行緒。

我們可以計算出程式所需執行緒的總數,總結如下:

執行緒數 = CPU可用核心數/(1 - 阻塞係數),其中阻塞係數的取值在0和1之間。

計算密集型人物的阻塞係數為0,而IO密集型任務的阻塞係數則接近1。

二、執行緒池的監控:

我們可以通過執行緒池提供的引數進行監控。執行緒池裡有一些屬性在監控執行緒池的時候可以使用

  • taskCount:執行緒池需要執行的任務數量。
  • completedTaskCount:執行緒池在執行過程中已完成的任務數量。小於或等於taskCount。
  • largestPoolSize:執行緒池曾經建立過的最大執行緒數量。通過這個資料可以知道執行緒池是否滿過。如等於執行緒池的最大大小,則表示執行緒池曾經滿了。
  • getPoolSize:執行緒池的執行緒數量。如果執行緒池不銷燬的話,池裡的執行緒不會自動銷燬,所以這個大小隻增不減。
  • getActiveCount:獲取活動的執行緒數。

通過擴充套件執行緒池進行監控。通過繼承執行緒池並重寫執行緒池的beforeExecute,afterExecute和terminated方法,我們可以在任務執行前,執行後和執行緒池關閉前幹一些事情。如監控任務的平均執行時間,最大執行時間和最小執行時間等。這幾個方法線上程池裡是空方法。如:

protected void beforeExecute(Thread t, Runnable r) { }

參考:《java虛擬機器併發程式設計》一書,《聊聊併發(三)——JAVA執行緒池的分析和使用