CPU 密集型任務
首先,我們來看 CPU 密集型任務,比如:加密、解密、壓縮、計算等一系列需要大量耗費 CPU 資源的任務。
對於這樣的任務最佳的線程數為 CPU 核心數的 1~2 倍,如果設置過多的線程數,實際上並不會起到很好的效果。
此時假設我們設置的線程數量是 CPU 核心數的 > 2 倍,因為計算任務非常重,會占用大量的 CPU 資源,所以這時 CPU 的每個核心工作基本都是滿負荷的,而我們又設置了過多的線程,每個線程都想去利用 CPU 資源來執行自己的任務,這就會造成不必要的上下文切換,此時線程數的增多並沒有讓性能提升,反而由於線程數量過多會導致性能下降。
針對這種情況,我們最好還要同時考慮在同一台機器上還有哪些其他會占用過多 CPU 資源的程序在運行,然后對資源使用做整體的平衡。
耗時 IO 型任務
第二種任務是耗時 IO 型,比如:數據庫、文件的讀寫,網絡通信等任務,這種任務的特點是並不會特別消耗 CPU 資源,但是 IO 操作很耗時,總體會占用比較多的時間。
對於這種任務最大線程數一般會大於 CPU 核心數很多倍,因為 IO 讀寫速度相比於 CPU 的速度而言是比較慢的,如果我們設置過少的線程數,就可能導致 CPU 資源的浪費。
而如果我們設置更多的線程數,那么當一部分線程正在等待 IO 的時候,它們此時並不需要 CPU 來計算,那么另外的線程便可以利用 CPU 去執行其他的任務,互不影響,這樣的話在任務隊列中等待的任務就會減少,可以更好地利用資源。
《Java並發編程實戰》的作者 Brain Goetz 推薦的計算方法:
線程數 = CPU 核心數 *(1+平均等待時間/平均工作時間)
通過這個公式,我們可以計算出一個合理的線程數量,如果任務的平均等待時間長,線程數就隨之增加,而如果平均工作時間長,也就是對於我們上面的 CPU 密集型任務,線程數就隨之減少。
太少的線程數會使得程序整體性能降低,而過多的線程也會消耗內存等其他資源,
所以如果想要更准確的話,可以進行壓測,監控 JVM 的線程情況以及 CPU 的負載情況,根據實際情況衡量應該創建的線程數,合理並充分利用資源。
結論
綜上所述我們就可以得出一個結論:
-
線程的平均工作時間所占比例越高,就需要越少的線程;
-
線程的平均等待時間所占比例越高,就需要越多的線程;
-
針對不同的程序,進行對應的實際測試就可以得到最合適的選擇。