文章目錄
- 前言
- 一、執行緒池的實作原理
- 1、當提交一個任務到執行緒池時,執行緒池的處理流程
- 以上流程的圖解
- 2、`ThreadPoolExecutor`執行`executor()`方法的`四種`情況
- 3、執行緒池里的執行緒執行任務分兩種情況
- 二、執行緒池的使用
- 1、執行緒池的創建
- 1.1、new 一個ThreadPoolExecutor呼叫的構造方法的原始碼
- 1.2、ThreadPoolExecutor構造方法的引數決議
- 1.3、常用的阻塞佇列
- 1.4、四種飽和策略
- 2、向執行緒池提交任務
- 2.1、execute()方法
- 2.2、submit()方法
- 3、自定義一個執行緒池
- 三、關閉執行緒池
- 1、 使用`shutdown`關閉執行緒池
- 2、 使用`shutdownNow`關閉執行緒池
- 3、 兩種關閉的原理
- 四、合理的配置執行緒池
- 1、 CPU密集型任務
- 2、 IO密集型任務
- 3、 混合型任務
- 4、 一般計算設定的執行緒數的公式
- 五、合理使用執行緒池帶來的三個好處
- 1、降低資源的消耗
- 2、提高回應速度
- 3、提高執行緒的可管理性
- 六、知識腦圖
- 謝謝各位的一鍵三連
前言
最近在準備面試資料,就看了一下多執行緒相關的文章,但是找了很多文章發現大家寫的差不多一樣,有的文章同樣的內容,但是給出的答案卻是不一樣的,所有我就自己參考一些自己買的書籍和看一下原始碼總結了一下,也就是以下內容,
這里都是執行緒池相關的內容,也就是ThreadPoolExecutor這個類相關的內容,至于Executors框架,我會單獨再寫一篇文章進行詳細的寫一下,也作為自己的面試總結,

一、執行緒池的實作原理
1、當提交一個任務到執行緒池時,執行緒池的處理流程
1、判斷核心執行緒池里的核心執行緒是否已滿
- 是,判斷作業佇列是否已經滿了
- 否,不管核心執行緒有沒有空閑,都創建一個新的執行緒執行這個任務
2、作業佇列是否已經滿了
- 已滿,判斷執行緒池是否滿了
- 未滿,將任務存盤在作業佇列里
3、判斷執行緒池是否滿了【核心執行緒以外的可以創建的執行緒,最大執行緒數】
- 已滿,交給飽和策略
handle處理無法執行的任務- 未滿,創建一個新的執行緒執行這個任務
以上流程的圖解
1、 執行緒池剛創建時是沒有執行緒的,并且里面包含了一個任務佇列,
2、 小于核心執行緒數(
corePoolSize),不管有沒有空閑的核心執行緒任務來了都創建一個新的執行緒;3、 大于等于核心執行緒數(
corePoolSize)且小于設定的最大執行緒數(maximumPoolSize),先加入到任務佇列(workQueue)里;4、 佇列滿了再繼續創建執行緒,直到執行緒數到達設定的最大執行緒數(
maximumPoolSize);5、 如果達到最大執行緒數(
maximumPoolSize)且任務佇列(workQueue)滿了,則通過 handler 所指定的飽和策略來處理此任務;6、 當執行緒池中的執行緒數量大于核心執行緒數(
maximumPoolSize)時,如果某個執行緒空閑時間超過keepAliveTime,執行緒將被終止,這樣執行緒池可以動態的調整池中的執行緒數,
2、ThreadPoolExecutor執行executor()方法的四種情況
1、 如果當前運行的執行緒少于
corePoolSize,則創建新的執行緒來執行任務【全域鎖】2、 如果運行的執行緒數大于等于
corePoolSize,則將任務加入BlockingQueue阻塞佇列3、 如果
BlockingQueue阻塞佇列已滿,無法加入新的任務,則創建新的執行緒來處理【全域鎖】4、 如果創建的新的執行緒促使正在運行的執行緒數超過了
maximumPoolSize,任務將被拒絕,并呼叫RejectedExecutionHandler的rejectedExecution()方法
3、執行緒池里的執行緒執行任務分兩種情況
1、在
executor()方法中創建一個執行緒,會讓這個執行緒執行當前任務2、執行緒在執行完當前任務以后,佇列里有任務就會不停的從
BlockingQueue中取任務執行,
二、執行緒池的使用
1、執行緒池的創建
1.1、new 一個ThreadPoolExecutor呼叫的構造方法的原始碼
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
Executor框架中創建的額執行緒池,都是基于該構造方法創建的,
1.2、ThreadPoolExecutor構造方法的引數決議
1、int corePoolSize
執行緒池的基本大小
當提交一個執行緒的時候,執行緒池會創建一個新的基本執行緒來執行任務,即使其他空閑的基本執行緒能夠執行新的任務也會創建基本執行緒
除非需要執行的任務數大于執行緒池的基本大小,才不會創建新的基本執行緒
呼叫執行緒池的
prestartAllCoreThreads()方法執行緒池會提前創建并啟動所有的基本執行緒
2、int maximumPoolSize
執行緒池允許創建的最大執行緒數
如果佇列滿了,并且已經創建的執行緒數小于最大執行緒數,則執行緒池會再創建新的執行緒執行任務,
無界佇列時,這個引數沒有意義
3、long keepAliveTime
執行緒活動保持的時間
執行緒池的作業執行緒空閑后,保持存活的時間,
如果任務多,每個任務的執行時間短,可以調大時間,提高執行緒的利用率
4、TimeUnit unit
- 執行緒活動保持的時間的單位
5、BlockingQueue workQueue
- 用于保持等待執行任務的阻塞佇列
6、ThreadFactory threadFactory
- 用于設定創建執行緒的工廠
- 可以通過執行緒工廠給每個創建出來的執行緒設定個名字
7、RejectedExecutionHandler handler
- 飽和策略,就是佇列和執行緒都滿了以后的策略
- 默認的策略是
AbortPolicy,表示無法處理新的任務時候拋出【RejectedExecutionException】例外,
1.3、常用的阻塞佇列
ArrayBlockingQueue
- 有界阻塞佇列
- 基于陣列結構
- 按照先進先出【
FIFO】原則對元素排序
LinkedBlockingQueue
- 無界阻塞佇列
- 基于鏈表結構
- 按照先進先出【
FIFO】原則對元素排序- 吞吐量高于
ArrayBlockingQueueExecutors.newFixedThreadPool()使用的這個佇列
SynchronousQueue
- 不存盤元素的阻塞佇列
- 每一個插入操作必須等到另一個執行緒呼叫移除操作
- 沒有容量,不存盤元素的阻塞佇列,也即單個元素的佇列,每一個put操作必須要等待一個take操作,否則不能繼續添加元素
- 否則插入操作一直處于阻塞狀態
- 吞吐量高于
SynchronousQueueExecutors.newCachedThreadPool()使用的這個佇列
PriorityBlockingQueue
- 無界阻塞佇列
- 具有優先級
- 內部使用陣列存盤資料,達到容量時,會自動進行擴容,放入的元素會按照優先級進行排序
1.4、四種飽和策略
1、AbortPolicy
- 該策略是執行緒池的默認策略,使用該策略時,如果執行緒池佇列滿了丟掉這個任務并且拋出
RejectedExecutionException例外,
2、CallerRunsPolicy
只有呼叫者所在的執行緒來運行該任務,
誰呼叫,誰執行,
3、DiscardOldestPolicy
將最早進入佇列的任務洗掉,之后再嘗試加入佇列,
因為是佇列嗎,先從任務佇列彈出最先加入的任務,空出一個位置,然后再次執行
execute方法把任務加入佇列,
4、DiscardPolicy
- 不做任何處理,直接丟掉這個任務,不會有例外拋出,
2、向執行緒池提交任務
2.1、execute()方法
1、
execute()方法用于提交不需要回傳值的任務2、無法判斷任務是否被執行緒池執行成功
2.2、submit()方法
1、
submit()方法用于提交需要回傳值的任務,會回傳一個future型別的物件2、通過
future型別的物件可以判斷任務是否執行成功3、也可以通過
future型別的物件的get()方法獲取回傳值4、
future型別的物件的get()方法會阻塞當前執行緒知道任務完成
3、自定義一個執行緒池
private static final int THREAD_POOL_SIZE = 5;
public static ThreadPoolExecutor threadPool(){
// 使用 ThreadFactoryBuilder 創建自定義執行緒名稱的 ThreadFactory,需要引入guava 的maven依賴
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("mine-task-pool-%d").build();
// 創建執行緒池,其中任務佇列需要結合實際情況設定合理的容量
return new ThreadPoolExecutor(THREAD_POOL_SIZE,
THREAD_POOL_SIZE,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(8),
namedThreadFactory,
new ThreadPoolExecutor.AbortPolicy());
}
三、關閉執行緒池
1、 使用shutdown關閉執行緒池
將執行緒池狀態置為
SHUTDOWN狀態,并不會立即停止內部正在跑的任務和佇列里等待的任務,會執行完
中斷所有沒有正在執行任務的執行緒,也不會接收新的任務
2、 使用shutdownNow關閉執行緒池
將執行緒池狀態置為
STOP先停止接收外部提交的任務
嘗試停止所有正在執行任務的執行緒和暫停任務的執行緒
嘗試停止并不是立即停止,嘗試將正在跑的任務interrupt中斷
回傳等待執行任務的串列
3、 兩種關閉的原理
- 遍歷執行緒池中的作業執行緒,然后逐個呼叫執行緒的
interrupt方法來中斷執行緒,- 無法中斷回應中斷的任務可能永遠無法終止,
四、合理的配置執行緒池
1、 CPU密集型任務
CPU密集型也叫計算密集型,指的是系統的硬碟、記憶體性能相對CPU要好很多,此時,系統運作大部分的狀況是CPU Loading 100%,CPU要讀/寫I/O(硬碟/記憶體),I/O在很短的時間就可以完成,而CPU還有許多運算要處理,CPU Loading很高,- 應配置盡可能少的執行緒,如配置
CPU個數加1個執行緒的執行緒池,
2、 IO密集型任務
IO密集型指的是系統的CPU性能相對硬碟、記憶體要好很多,此時,系統運作,大部分的狀況是CPU在等I/O(硬碟/記憶體) 的讀/寫操作,此時CPU Loading并不高,執行緒并不是一直在執行任務,要配置盡可能多的執行緒,如2倍
CPU個數執行緒的執行緒池
3、 混合型任務
- 拆分為
CPU密集型任務和IO密集型任務
4、 一般計算設定的執行緒數的公式
- 執行緒數=
cpu核數*(cpu計算時間+io等待時間)/cpu計算時間
五、合理使用執行緒池帶來的三個好處
1、降低資源的消耗
- 重復利用已經創建的執行緒,降低執行緒的創建和銷毀帶來的消耗
2、提高回應速度
- 當任務到達的時候,任務不需要等待執行緒的創建可以直接使用執行緒池里已經創建的執行緒
3、提高執行緒的可管理性
- 執行緒池可以進行統一的分配、調優和監控
六、知識腦圖

謝謝各位的一鍵三連
- 創作不易, 非常歡迎大家的點贊、評論和關注(^_?)☆
- 你的點贊、評論以及關注是對我最大的支持和鼓勵
- 是我繼續創作高質量博客的動力 !!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/287601.html
標籤:java

