線程池
-
- 線程池概述
-
- 1.線程池的好處
- 2.線程池的圖解
- 3.線程池的運作過程
- 線程池解析
-
- 1. 阻塞隊列
- 2. handler:拒絕處理政策
- 3. 如何設定線程池參數(僅供參考)
- 常用的線程池
-
- 1. newCachedThreadPool()
- 2 . newFixedThreadPool(int nThreads)
- 3. newSingleThreadExecutor()
線程池概述
就是把一堆線程建立好了放在一個容器中(池子裡),需要用的時候就直接拿出來用,用完之後再放回池子裡。
1.線程池的好處
- 降低資源消耗,降低了頻繁建立線程和銷毀線程的開銷
- 提高響應速度
- 提高線程的可管理性,可以對線程進行一些操作,友善管理線程
2.線程池的圖解
3.線程池的運作過程
線程池解析
線程池的最上層接口是Executor,這個接口定義了一個核心方法execute(Runnabel command),這個方法最後被ThreadPoolExecutor類實作,這個方法是用來傳入任務的。而且ThreadPoolExecutor是線程池的核心類,此類的構造方法如下:
1. 阻塞隊列
用來建立線程,一般有三種選擇的阻塞隊列:
- ArrayBlockingQueue:有界隊列(基于數組的)
- LinkedBlockingQueue:有/無界隊列(基于連結清單的,傳參就是有界,不傳就是無界)
- SynchronousQueue:同步移交隊列(需要一個線程調用put方法插入值,另一個線程調用take方法删除值)
2. handler:拒絕處理政策
當阻塞隊列滿了且線程數量大于最大線程數就會采用拒絕處理政策,四種政策為:
- ThreadPoolExecutor.AbortPolicy(預設):丢棄任務并抛出RejectedExecutionException異常
這是線程池預設的拒絕政策,在任務不能再送出的時候,抛出異常,及時回報程式運作狀态。如果是比較關鍵的業務,推薦使用此拒絕政策,這樣子在系統不能承載更大的并發量的時候,能夠及時的通過異常發現。
- ThreadPoolExecutor.DiscardPolicy:也是丢棄任務,但是不抛出異常
使用此政策,可能會使我們無法發現系統的異常狀态。建議是一些無關緊要的業務采用此政策。例如,本人的部落格網站統計閱讀量就是采用的這種拒絕政策。
- ThreadPoolExecutor.DiscardOldestPolicy:丢棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
此拒絕政策,是一種喜新厭舊的拒絕政策。是否要采用此種拒絕政策,還得根據實際業務是否允許丢棄老任務來認真衡量。
- 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();
}
}
});
}
}
}