天天看點

線程池原理及常用線程池介紹

線程池

    • 線程池概述
      • 1.線程池的好處
      • 2.線程池的圖解
      • 3.線程池的運作過程
    • 線程池解析
      • 1. 阻塞隊列
      • 2. handler:拒絕處理政策
      • 3. 如何設定線程池參數(僅供參考)
    • 常用的線程池
      • 1. newCachedThreadPool()
      • 2 . newFixedThreadPool(int nThreads)
      • 3. newSingleThreadExecutor()

線程池概述

就是把一堆線程建立好了放在一個容器中(池子裡),需要用的時候就直接拿出來用,用完之後再放回池子裡。

1.線程池的好處

  1. 降低資源消耗,降低了頻繁建立線程和銷毀線程的開銷
  2. 提高響應速度
  3. 提高線程的可管理性,可以對線程進行一些操作,友善管理線程

2.線程池的圖解

線程池原理及常用線程池介紹

3.線程池的運作過程

線程池原理及常用線程池介紹

線程池解析

線程池的最上層接口是Executor,這個接口定義了一個核心方法execute(Runnabel command),這個方法最後被ThreadPoolExecutor類實作,這個方法是用來傳入任務的。而且ThreadPoolExecutor是線程池的核心類,此類的構造方法如下:

線程池原理及常用線程池介紹

1. 阻塞隊列

用來建立線程,一般有三種選擇的阻塞隊列:

  • ArrayBlockingQueue:有界隊列(基于數組的)
  • LinkedBlockingQueue:有/無界隊列(基于連結清單的,傳參就是有界,不傳就是無界)
  • SynchronousQueue:同步移交隊列(需要一個線程調用put方法插入值,另一個線程調用take方法删除值)

2. handler:拒絕處理政策

當阻塞隊列滿了且線程數量大于最大線程數就會采用拒絕處理政策,四種政策為:

  1. ThreadPoolExecutor.AbortPolicy(預設):丢棄任務并抛出RejectedExecutionException異常
    這是線程池預設的拒絕政策,在任務不能再送出的時候,抛出異常,及時回報程式運作狀态。如果是比較關鍵的業務,推薦使用此拒絕政策,這樣子在系統不能承載更大的并發量的時候,能夠及時的通過異常發現。
  2. ThreadPoolExecutor.DiscardPolicy:也是丢棄任務,但是不抛出異常
    使用此政策,可能會使我們無法發現系統的異常狀态。建議是一些無關緊要的業務采用此政策。例如,本人的部落格網站統計閱讀量就是采用的這種拒絕政策。
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
    此拒絕政策,是一種喜新厭舊的拒絕政策。是否要采用此種拒絕政策,還得根據實際業務是否允許丢棄老任務來認真衡量。
  4. ThreadPoolExecutor.CallerRunsPolicy:由調用線程(送出任務的線程)直接執行被丢棄的任務
    此拒絕政策,可以用于必須執行任務不允許任務丢失的場景

3. 如何設定線程池參數(僅供參考)

1.根據伺服器的類型來設定

  • IO阻塞型:線程數可以設定大一點,線程數 = CPU核數 * 1 / (1 - 阻塞系數)
  • CPU密集型:線程數 = CPU核數(一般情況下,也可以根據系統情況做相應的調整)

阻塞系數:

IO密集型:阻塞系數趨向于1,如果為0.9,則線程數 = CPU核數 * 10;如果為0.99,則線程數 = CPU核數 *100

CPU密集型:阻塞系數趨向于0,線程數 = CPU核數

2.無法判斷伺服器類型(通常為混合型)

  • 通過模拟生産環境來進行壓測,不斷調節線程參數和并發數,進而獲得可靠資料(推薦)

常用的線程池

1. newCachedThreadPool()

線程池原理及常用線程池介紹
核心線程數為0,最大線程數幾乎無限,阻塞隊列使用的是 SynchronousQueue:同步移交隊列

注意事項:使用時要注意控制線程的數量,防止因線程數太多導緻OOM。

public class ThreadPoolExecutorTest {
 	public static void main(String[] args) {
  	ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
 		 for (int i = 0; i < 10; i++) {
  		 final int index = i;
   			try {
    		Thread.sleep(index * 1000);
  			} catch (InterruptedException e) {
   			e.printStackTrace();
   }
   cachedThreadPool.execute(() -> System.out.println(index);
});
  		}
    }
}
           

2 . newFixedThreadPool(int nThreads)

建立一個指定工作線程數量的線程池。每當送出一個任務就建立一個工作線程,如果工作線程數量達到線程池初始的最大數,則将送出的任務存入到池隊列中。

FixedThreadPool是一個典型且優秀的線程池,它具有線程池提高程式效率和節省建立線程時所耗的開銷的優點。但是,線上程池空閑時,即線程池中沒有可運作任務時,它不會釋放工作線程,還會占用一定的系統資源。

線程池原理及常用線程池介紹
線程數量固定,傳入的即是核心線程數,也是最大值;阻塞隊列用的是無界的。

注意:使用時要注意控制無界隊列的大小,防止因隊列中等待的線程數太多導緻OOM。

public class ThreadPoolExecutorTest {
 	public static void main(String[] args) {
 	ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
  	for (int i = 0; i < 10; i++) {
   		final int index = i;
   		fixedThreadPool.execute(() -> {
     	try {
      		System.out.println(index);
      		Thread.sleep(2000);
    	 } catch (InterruptedException e) {
    	  	e.printStackTrace();
     }
    }
   });
  }
 }
}
           

3. newSingleThreadExecutor()

線程池原理及常用線程池介紹

核心線程數和最大線程數都是1,阻塞隊列是無限的(單一線程,一次執行一個線程)

public class ThreadPoolExecutorTest {
 	public static void main(String[] args) {
  	ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
  	for (int i = 0; i < 10; i++) {
  	final int index = i;
   	singleThreadExecutor.execute(() -> {
     try {
      System.out.println(index);
      Thread.sleep(2000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    }
   });
  }
 }
}