一、確定線程數
在工作中,爲了加快程序的處理速度,我們需要將問題分解成若干個併發執行的任務。接着我們將這些任務委派給線程,以便使它們可以併發的執行。但是需要注意的是,由於資源所限,我們不能創建過多的線程。
這就涉及到一個 確定創建多少線程數纔是合理 的問題。
《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線程池的分析和使用》