我相信你們都用過線程池,可是線程池數量設置爲多少比較合理呢?數據庫
線程數的設置的最主要的目的是爲了充分併合理地使用 CPU 和內存等資源,從而最大限度地提升程序的性能,所以讓咱們一塊兒去探索吧!編程
首先要考慮到 CPU 核心數,那麼在 Java 中如何獲取核心線程數?網絡
可使用 Runtime.getRuntime().availableProcessor()
方法來獲取(可能不許確,做爲參考)多線程
在確認了核心數後,再去判斷是 CPU 密集型任務仍是 IO 密集型任務:併發
在知道如何判斷任務的類別後,讓咱們分兩個場景進行討論:工具
對於 CPU 密集型計算,多線程本質上是提高多核 CPU 的利用率,因此對於一個 8 核的 CPU,每一個核一個線程,理論上建立 8 個線程就能夠了。性能
若是設置過多的線程數,實際上並不會起到很好的效果。此時假設咱們設置的線程數量是 CPU 核心數的 2 倍,由於計算任務很是重,會佔用大量的 CPU 資源,因此這時 CPU 的每一個核心工做基本都是滿負荷的,而咱們又設置了過多的線程,每一個線程都想去利用 CPU 資源來執行本身的任務,這就會形成沒必要要的上下文切換,此時線程數的增多並無讓性能提高,反而因爲線程數量過多會致使性能降低。測試
所以,對於 CPU 密集型的計算場景,理論上線程的數量 = CPU 核數就是最合適的,不過一般把線程的數量設置爲CPU 核數 +1,會實現最優的利用率。即便當密集型的線程因爲偶爾的內存頁失效或其餘緣由致使阻塞時,這個額外的線程也能確保 CPU 的時鐘週期不會被浪費,從而保證 CPU 的利用率。線程
以下圖就是在一個 8 核 CPU 的電腦上,經過修改線程數來測試對 CPU 密集型任務(素數計算)的性能影響。3d
能夠看到線程數小於 8 時,性能是不好的,在線程數多於處理器核心數對性能的提高也很小,所以能夠驗證公式仍是具備必定適用性的。
除此以外,咱們最好還要同時考慮在同一臺機器上還有哪些其餘會佔用過多 CPU 資源的程序在運行,而後對資源使用作總體的平衡。
對於 IO 密集型任務最大線程數通常會大於 CPU 核心數不少倍,由於 IO 讀寫速度相比於 CPU 的速度而言是比較慢的,若是咱們設置過少的線程數,就可能致使 CPU 資源的浪費。而若是咱們設置更多的線程數,那麼當一部分線程正在等待 IO 的時候,它們此時並不須要 CPU 來計算,那麼另外的線程即可以利用 CPU 去執行其餘的任務,互不影響,這樣的話在任務隊列中等待的任務就會減小,能夠更好地利用資源。
對於 IO 密集型計算場景,最佳的線程數是與程序中 CPU 計算和 IO 操做的耗時比相關的,《Java併發編程實戰》的做者 Brain Goetz 推薦的計算方法以下:
線程數 = CPU 核心數 * (1 + IO 耗時/ CPU 耗時)
經過這個公式,咱們能夠計算出一個合理的線程數量,若是任務的平均等待時間長,線程數就隨之增長,而若是平均工做時間長,也就是對於咱們上面的 CPU 密集型任務,線程數就隨之減小。能夠採用 APM 工具統計到每一個方法的耗時,便於計算 IO 耗時和 CPU 耗時。
在這裏引用Java併發編程實戰中的圖,方便你們更容易理解:
還有一派的計算方式是《Java虛擬機併發編程》中提出的:
線程數 = CPU 核心數 / (1 - 阻塞係數)
其中計算密集型阻塞係數爲 0,IO 密集型阻塞係數接近 1,通常認爲在 0.8 ~ 0.9 之間。好比 8 核 CPU,按照公式就是 2 / ( 1 - 0.9 ) = 20 個線程數
上圖是 IO 密集型任務的一個測試,是在雙核處理器上開不一樣的線程數(從 1 到 40)來測試對程序性能的影響,能夠看到線程池數量達到 20 以後,曲線逐漸水平,說明開再多的線程對程序的性能提高也毫無幫助。
太少的線程數會使得程序總體性能下降,而過多的線程也會消耗內存等其餘資源,因此若是想要更準確的話,能夠進行壓測,監控 JVM 的線程狀況以及 CPU 的負載狀況,根據實際狀況衡量應該建立的線程數,合理並充分利用資源。
同時,有不少線程池的應用,好比 Tomcat、Redis、Jdbc 等,每一個應用設置的線程數也是不一樣的,好比 Tomcat 爲流量入口,那麼線程數的設置可能就要比其餘應用要大。
經過對線程數設置的探究,咱們能夠得知線程數的設置首先和 CPU 核心數有莫大關聯,除此以外,咱們須要根據任務類型的不一樣選擇對應的策略,線程的平均工做時間所佔比例越高,就須要越少的線程;線程的平均等待時間所佔比例越高,就須要越多的線程;針對不一樣的程序,進行對應的實際測試就能夠獲得最合適的選擇。
參考
《Java併發編程實戰》
《Java虛擬機併發編程》
Java併發編程實戰
Java併發編程核心