點擊上方「武培軒」,選擇「設爲星標」web
技術文章第一時間送達!數據庫
我相信你們都用過線程池,可是線程池數量設置爲多少比較合理呢?編程
線程數的設置的最主要的目的是爲了充分併合理地使用 CPU 和內存等資源,從而最大限度地提升程序的性能,所以讓咱們一塊兒去探索吧!微信
首先要考慮到 CPU 核心數,那麼在 Java 中如何獲取核心線程數?網絡
可使用 Runtime.getRuntime().availableProcessor()
方法來獲取(可能不許確,做爲參考)多線程
在確認了核心數後,再去判斷是 CPU 密集型任務仍是 IO 密集型任務:併發
-
CPU 密集型任務:好比像加解密,壓縮、計算等一系列須要大量耗費 CPU 資源的任務, 大部分場景下都是純 CPU 計算。 -
IO 密集型任務:好比像 MySQL 數據庫、文件的讀寫、網絡通訊等任務,這類任務 不會特別消耗 CPU 資源,可是 IO 操做比較耗時,會佔用比較多時間。
在知道如何判斷任務的類別後,讓咱們分兩個場景進行討論:app
CPU 密集型任務
對於 CPU 密集型計算,多線程本質上是提高多核 CPU 的利用率,因此對於一個 8 核的 CPU,每一個核一個線程,理論上建立 8 個線程就能夠了。編輯器
若是設置過多的線程數,實際上並不會起到很好的效果。此時假設咱們設置的線程數量是 CPU 核心數的 2 倍,由於計算任務很是重,會佔用大量的 CPU 資源,因此這時 CPU 的每一個核心工做基本都是滿負荷的,而咱們又設置了過多的線程,每一個線程都想去利用 CPU 資源來執行本身的任務,這就會形成沒必要要的上下文切換,此時線程數的增多並無讓性能提高,反而因爲線程數量過多會致使性能降低。工具
所以,對於 CPU 密集型的計算場景,理論上線程的數量 = CPU 核數就是最合適的,不過一般把線程的數量設置爲CPU 核數 +1,會實現最優的利用率。即便當密集型的線程因爲偶爾的內存頁失效或其餘緣由致使阻塞時,這個額外的線程也能確保 CPU 的時鐘週期不會被浪費,從而保證 CPU 的利用率。
以下圖就是在一個 8 核 CPU 的電腦上,經過修改線程數來測試對 CPU 密集型任務(素數計算)的性能影響。
能夠看到線程數小於 8 時,性能是不好的,在線程數多於處理器核心數對性能的提高也很小,所以能夠驗證公式仍是具備必定適用性的。
除此以外,咱們最好還要同時考慮在同一臺機器上還有哪些其餘會佔用過多 CPU 資源的程序在運行,而後對資源使用作總體的平衡。
IO 密集型任務
對於 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 之間。好比 雙 核 CPU,按照公式就是 2 / ( 1 - 0.9 ) = 20 個線程數
上圖是 IO 密集型任務的一個測試,是在雙核處理器上開不一樣的線程數(從 1 到 40)來測試對程序性能的影響,能夠看到線程池數量達到 20 以後,曲線逐漸水平,說明開再多的線程對程序的性能提高也毫無幫助。
太少的線程數會使得程序總體性能下降,而過多的線程也會消耗內存等其餘資源,因此若是想要更準確的話,能夠進行壓測,監控 JVM 的線程狀況以及 CPU 的負載狀況,根據實際狀況衡量應該建立的線程數,合理並充分利用資源。
同時,有不少線程池的應用,好比 Tomcat、Redis、Jdbc 等,每一個應用設置的線程數也是不一樣的,好比 Tomcat 爲流量入口,那麼線程數的設置可能就要比其餘應用要大。
總結
經過對線程數設置的探究,咱們能夠得知線程數的設置首先和 CPU 核心數有莫大關聯,除此以外,咱們須要根據任務類型的不一樣選擇對應的策略,線程的平均工做時間所佔比例越高,就須要越少的線程;線程的平均等待時間所佔比例越高,就須要越多的線程;針對不一樣的程序,進行對應的實際測試就能夠獲得最合適的選擇。
參考
《Java併發編程實戰》
《Java虛擬機併發編程》
Java併發編程實戰
Java併發編程核心
完
本文分享自微信公衆號 - 武培軒(wupeixuan404)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。