1. 程式人生 > 程式設計 >JAVA 自定義執行緒池的最大執行緒數設定方法

JAVA 自定義執行緒池的最大執行緒數設定方法

一:CPU密集型:

  定義:CPU密集型也是指計算密集型,大部分時間用來做計算邏輯判斷等CPU動作的程式稱為CPU密集型任務。該型別的任務需要進行大量的計算,主要消耗CPU資源。 這種計算密集型任務雖然也可以用多工完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,所以,要最高效地利用CPU,計算密集型任務同時進行的數量應當等於CPU的核心數。

  特點:

     01:CPU 使用率較高(也就是經常計算一些複雜的運算,邏輯處理等情況)非常多的情況下使用

     02:針對單臺機器,最大執行緒數一般只需要設定為CPU核心數的執行緒個數就可以了

     03:這一型別多出現在開發中的一些業務複雜計算和邏輯處理過程中。

  程式碼示例:

package pool;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Demo02 {
  public static void main(String[] args) {
    //自定義執行緒池! 工作中只會使用 ThreadPoolExecutor

    /**
     * 最大執行緒該如何定義(執行緒池的最大的大小如何設定!)
     * 1、CPU 密集型,幾核,就是幾,可以保持CPU的效率最高!
     */

    //獲取電腦CPU核數
    System.out.println(Runtime.getRuntime().availableProcessors());  //8核

    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
        2,      //核心執行緒池大小
        Runtime.getRuntime().availableProcessors(),//最大核心執行緒池大小(CPU密集型,根據CPU核數設定)
        3,       //超時了沒有人呼叫就會釋放
        TimeUnit.SECONDS,//超時單位
        new LinkedBlockingDeque<>(3),//阻塞佇列
        Executors.defaultThreadFactory(),//執行緒工廠,建立執行緒的,一般不用動
        new ThreadPoolExecutor.AbortPolicy());    //銀行滿了,還有人進來,不處理這個人的,丟擲異常

    try {
      //最大承載數,Deque + Max  (佇列執行緒數+最大執行緒數)
      //超出 丟擲 RejectedExecutionException 異常
      for (int i = 1; i <= 9; i++) {
        //使用了執行緒池之後,使用執行緒池來建立執行緒
        threadPool.execute(()->{
          System.out.println(Thread.currentThread().getName()+" ok");
        });
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      //執行緒池用完,程式結束,關閉執行緒池
      threadPool.shutdown();   //(為確保關閉,將關閉方法放入到finally中)
    }
  }
}

二: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 時間所佔比例越高,需要越少的執行緒,這一型別在開發中主要出現在一些計算業務頻繁的邏輯中。

  程式碼示例:

package pool;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Demo02 {
  public static void main(String[] args) {
    //自定義執行緒池! 工作中只會使用 ThreadPoolExecutor

    /**
     * 最大執行緒該如何定義(執行緒池的最大的大小如何設定!)
     * 2、IO  密集型 >判斷你程式中十分耗IO的執行緒
     *   程式  15個大型任務  io十分佔用資源! (最大執行緒數設定為30)
     *   設定最大執行緒數為十分耗io資源執行緒個數的2倍
     */

    //獲取電腦CPU核數
    System.out.println(Runtime.getRuntime().availableProcessors());  //8核

    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
        2,//核心執行緒池大小
        16,              //若一個IO密集型程式有15個大型任務且其io十分佔用資源!(最大執行緒數設定為 2*CPU 數目)
        3,//超時了沒有人呼叫就會釋放
        TimeUnit.SECONDS,//執行緒工廠,建立執行緒的,一般不用動
        new ThreadPoolExecutor.DiscardOldestPolicy()); //佇列滿了,嘗試和最早的競爭,也不會丟擲異常

    try {
      //最大承載數,Deque + Max  (佇列執行緒數+最大執行緒數)
      //超出 丟擲 RejectedExecutionException 異常
      for (int i = 1; i <= 9; i++) {
        //使用了執行緒池之後,使用執行緒池來建立執行緒
        threadPool.execute(()->{
          System.out.println(Thread.currentThread().getName()+" ok");
        });
      }
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      //執行緒池用完,程式結束,關閉執行緒池
      threadPool.shutdown();   //(為確保關閉,將關閉方法放入到finally中)
    }
  }
}

接下來我們進行一一分析:

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核心數的兩倍是最佳的。

以上就是JAVA 自定義執行緒池的最大執行緒數設定方法的詳細內容,更多關於JAVA 自定義執行緒池的最大執行緒數的資料請關注我們其它相關文章!