自定義執行緒池執行緒數量設定
一:CPU密集型:
定義:CPU密集型也是指計算密集型,大部分時間用來做計算邏輯判斷等CPU動作的程式稱為CPU密集型任務。該型別的任務需要進行大量的計算,主要消耗CPU資源。 這種計算密集型任務雖然也可以用多工完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,所以,要最高效地利用CPU,計算密集型任務同時進行的數量應當等於CPU的核心數。
特點:
01:CPU 使用率較高(也就是經常計算一些複雜的運算,邏輯處理等情況)非常多的情況下使用
02:針對單臺機器,最大執行緒數一般只需要設定為CPU核心數的執行緒個數就可以了
03:這一型別多出現在開發中的一些業務複雜計算和邏輯處理過程中。
程式碼示例:
1 package pool; 2 3 import java.util.concurrent.Executors; 4 import java.util.concurrent.LinkedBlockingDeque; 5 import java.util.concurrent.ThreadPoolExecutor; 6 import java.util.concurrent.TimeUnit; 7 8 public class Demo02 { 9 public static void main(String[] args) { 10 //自定義執行緒池! 工作中只會使用 ThreadPoolExecutor 11 12 /** 13 * 最大執行緒該如何定義(執行緒池的最大的大小如何設定!) 14 * 1、CPU 密集型,幾核,就是幾,可以保持CPU的效率最高! 15 */ 16 17 //獲取電腦CPU核數 18 System.out.println(Runtime.getRuntime().availableProcessors()); //8核 19 20 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 21 2, //核心執行緒池大小 22 Runtime.getRuntime().availableProcessors(), //最大核心執行緒池大小(CPU密集型,根據CPU核數設定) 23 3, //超時了沒有人呼叫就會釋放 24 TimeUnit.SECONDS, //超時單位 25 new LinkedBlockingDeque<>(3), //阻塞佇列 26 Executors.defaultThreadFactory(), //執行緒工廠,建立執行緒的,一般不用動 27 new ThreadPoolExecutor.AbortPolicy()); //銀行滿了,還有人進來,不處理這個人的,丟擲異常 28 29 try { 30 //最大承載數,Deque + Max (佇列執行緒數+最大執行緒數) 31 //超出 丟擲 RejectedExecutionException 異常 32 for (int i = 1; i <= 9; i++) { 33 //使用了執行緒池之後,使用執行緒池來建立執行緒 34 threadPool.execute(()->{ 35 System.out.println(Thread.currentThread().getName()+" ok"); 36 }); 37 } 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } finally { 41 //執行緒池用完,程式結束,關閉執行緒池 42 threadPool.shutdown(); //(為確保關閉,將關閉方法放入到finally中) 43 } 44 } 45 }
二:IO密集型:
定義:IO密集型任務指任務需要執行大量的IO操作,涉及到網路、磁碟IO操作,對CPU消耗較少,其消耗的主要資源為IO。
我們所接觸到的 IO ,大致可以分成兩種:磁碟 IO和網路 IO。
01:磁碟 IO ,大多都是一些針對磁碟的讀寫操作,最常見的就是檔案的讀寫,假如你的資料庫、 Redis 也是在本地的話,那麼這個也屬於磁碟 IO。
02:網路 IO ,這個應該是大家更加熟悉的,我們會遇到各種網路請求,比如 http 請求、遠端資料庫讀寫、遠端 Redis 讀寫等等。
IO 操作的特點就是需要等待,我們請求一些資料,由對方將資料寫入緩衝區,在這段時間中,需要讀取資料的執行緒根本無事可做,因此可以把 CPU 時間片讓出去,直到緩衝區寫滿。
既然這樣,IO 密集型任務其實就有很大的優化空間了(畢竟存在等待):
CPU 使用率較低,程式中會存在大量的 I/O 操作佔用時間,導致執行緒空餘時間很多,所以通常就需要開CPU核心數兩倍的執行緒。當執行緒進行 I/O 操作 CPU 空閒時,執行緒等待時間所佔比例越高,就需要越多執行緒,啟用其他執行緒繼續使用 CPU,以此提高 CPU 的使用率;執行緒 CPU 時間所佔比例越高,需要越少的執行緒,這一型別在開發中主要出現在一些計算業務頻繁的邏輯中。
程式碼示例:
1 package pool; 2 3 import java.util.concurrent.Executors; 4 import java.util.concurrent.LinkedBlockingDeque; 5 import java.util.concurrent.ThreadPoolExecutor; 6 import java.util.concurrent.TimeUnit; 7 8 public class Demo02 { 9 public static void main(String[] args) { 10 //自定義執行緒池! 工作中只會使用 ThreadPoolExecutor 11 12 /** 13 * 最大執行緒該如何定義(執行緒池的最大的大小如何設定!) 14 * 2、IO 密集型 >判斷你程式中十分耗IO的執行緒 15 * 程式 15個大型任務 io十分佔用資源! (最大執行緒數設定為30) 16 * 設定最大執行緒數為十分耗io資源執行緒個數的2倍 17 */ 18 19 //獲取電腦CPU核數 20 System.out.println(Runtime.getRuntime().availableProcessors()); //8核 21 22 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 23 2, //核心執行緒池大小 24 16, //若一個IO密集型程式有15個大型任務且其io十分佔用資源!(最大執行緒數設定為 2*CPU 數目) 25 3, //超時了沒有人呼叫就會釋放 26 TimeUnit.SECONDS, //超時單位 27 new LinkedBlockingDeque<>(3), //阻塞佇列 28 Executors.defaultThreadFactory(), //執行緒工廠,建立執行緒的,一般不用動 29 new ThreadPoolExecutor.DiscardOldestPolicy()); //佇列滿了,嘗試和最早的競爭,也不會丟擲異常 30 31 try { 32 //最大承載數,Deque + Max (佇列執行緒數+最大執行緒數) 33 //超出 丟擲 RejectedExecutionException 異常 34 for (int i = 1; i <= 9; i++) { 35 //使用了執行緒池之後,使用執行緒池來建立執行緒 36 threadPool.execute(()->{ 37 System.out.println(Thread.currentThread().getName()+" ok"); 38 }); 39 } 40 } catch (Exception e) { 41 e.printStackTrace(); 42 } finally { 43 //執行緒池用完,程式結束,關閉執行緒池 44 threadPool.shutdown(); //(為確保關閉,將關閉方法放入到finally中) 45 } 46 } 47 }
接下來我們進行一一分析:
1:高併發、任務執行時間短的業務,執行緒池執行緒數可以設定為CPU核數+1,減少執行緒上下文的切換
2:併發不高、任務執行時間長的業務這就需要區分開看了:
a)假如是業務時間長集中在IO操作上,也就是IO密集型的任務,因為IO操作並不佔用CPU,所以不要讓所有的CPU閒下來,可以適當加大執行緒池中的執行緒數目,讓CPU處理更多的業務
b)假如是業務時間長集中在計算操作上,也就是計算密集型任務,這個就沒辦法了,執行緒池中的執行緒數設定得少一些,減少執行緒上下文的切換
(其實從一二可以看出無論併發高不高,對於業務中是否是cpu密集還是I/O密集的判斷都是需要的當前前提是你需要優化效能的前提下)
3:併發高、業務執行時間長,解決這種型別任務的關鍵不在於執行緒池而在於整體架構的設計,看看這些業務裡面某些資料是否能做快取是第一步,我們的專案使用的時redis作為快取(這類非關係型資料庫還是挺好的)。增加伺服器是第二步(一般政府專案的首先,因為不用對專案技術做大改動,求一個穩,但前提是資金充足),至於執行緒池的設定,設定參考 2 。最後,業務執行時間長的問題,也可能需要分析一下,看看能不能使用中介軟體(任務時間過長的可以考慮拆分邏輯放入佇列等操作)對任務進行拆分和解耦。
三.:總結:
01:一個計算為主的程式(CPU密集型程式),多執行緒跑的時候,可以充分利用起所有的 CPU 核心數,比如說 8 個核心的CPU ,開8 個執行緒的時候,可以同時跑 8 個執行緒的運算任務,此時是最大效率。但是如果執行緒遠遠超出 CPU 核心數量,反而會使得任務效率下降,因為頻繁的切換執行緒也是要消耗時間的。因此對於 CPU 密集型的任務來說,執行緒數等於 CPU 數是最好的了。
02:如果是一個磁碟或網路為主的程式(IO密集型程式),一個執行緒處在 IO 等待的時候,另一個執行緒還可以在 CPU 裡面跑,有時候 CPU 閒著沒事幹,所有的執行緒都在等著 IO,這時候他們就是同時的了,而單執行緒的話此時還是在一個一個等待的。我們都知道 IO 的速度比起 CPU 來是很慢的。此時執行緒數等於CPU核心數的兩倍是最佳的。