詳解深入分析Java線程池源碼和底層原理
- 文章大綱
- 引言
- Java線程池概念及重要性
- `ThreadPoolExecutor`類的概述
- `ThreadPoolExecutor`類的基本功能和作用
- **基本功能**
- **核心作用**
- `ThreadPoolExecutor`主要構造函數及其參數
- 繼承關系鏈
- 功能介紹
- ThreadPoolExecutor 構造器
- 構造器參數
- 構造器中各個參數的含義
- corePoolSize
- maximumPoolSize
- keepAliveTime
- unit
- workQueue
- threadFactory
- handler
- 接口繼承關系鏈
- 總結與展望
文章大綱
引言
早期的編程實踐中,直接使用新線程執行任務雖直觀且易實現,但在高并發場景下卻面臨性能瓶頸。當系統需處理大量短暫并發任務時,頻繁創建和銷毀線程會導致巨大的開銷,降低系統性能,甚至可能因資源過度消耗導致系統崩潰。為解決線程頻繁創建和銷毀的問題,我們需要高效復用線程的機制。
Java線程池概念及重要性
Java的線程池(Thread Pool)提供了這樣的解決方案:預先創建并管理線程組,任務執行時從池中獲取線程,任務完成后線程返回池中等待新任務。這種方式顯著減少線程創建和銷毀,大幅提升系統并發處理能力。
ThreadPoolExecutor
類的概述
Java中的ThreadPoolExecutor
是線程池的核心實現,提供靈活的配置和管理方法。要深入理解其工作原理和使用,可從關鍵方法入手,逐步探索其實現邏輯。
ThreadPoolExecutor
類的基本功能和作用
在Java中,ThreadPoolExecutor
是線程池框架的核心組件,它允許開發者以高度可配置和靈活的方式管理線程資源。其核心功能是為應用程序提供一個線程池,從而優化和控制線程的使用,特別是在處理大量并發任務時。
基本功能
- 線程管理與復用:
ThreadPoolExecutor
管理一個線程池,當任務提交給線程池時,線程池會嘗試復用已有的線程來執行任務,而不是為每個新任務都創建一個新線程。這大大減少了線程創建和銷毀的開銷。 - 任務隊列:當線程池中的線程都在忙碌時,新提交的任務會被放置在一個任務隊列中等待執行。
ThreadPoolExecutor
允許你配置這個隊列的大小和類型,以適應不同的應用場景。 - 線程生命周期管理:
ThreadPoolExecutor
允許你配置線程的生命周期,包括核心線程數、最大線程數、線程空閑時間等。當線程空閑超過指定時間后,多余的線程會被銷毀以節省資源。 - 任務拒絕策略:當任務隊列已滿且所有線程都在忙碌時,新提交的任務將被拒絕。
ThreadPoolExecutor
提供了幾種內置的任務拒絕策略,同時也允許你自定義拒絕策略。
核心作用
- 提高性能:通過復用線程,
ThreadPoolExecutor
顯著減少了線程創建和銷毀的開銷,從而提高了應用程序的性能。 - 資源管理:
ThreadPoolExecutor
允許你精細控制線程資源的使用,包括線程的數量、任務的排隊策略等,從而更有效地管理系統的資源。 - 簡化并發編程:使用
ThreadPoolExecutor
,開發者可以更加專注于業務邏輯的實現,而無需過多關注線程的管理和調度。 - 提供靈活的擴展性:通過調整線程池的配置參數,
ThreadPoolExecutor
可以適應不同規模和需求的應用程序,提供了很好的擴展性。
ThreadPoolExecutor
主要構造函數及其參數
繼承關系鏈
ThreadPoolExecutor
實現了 ExecutorService
接口, ExecutorService
擴展了 Executor
接口。
功能介紹
-
Executor:這是Java中執行已提交任務的對象的接口,提供了一種將任務與任務執行機制(通常是線程)解耦的方式。
-
ExecutorService:這是一個擴展了
Executor
接口的接口,它提供了更全面的生命周期管理(例如關閉、終止)和任務提交機制(例如execute
,submit
等)。ExecutorService
通常用于控制和管理線程,它內部封裝了一組線程,使得線程的使用更加簡便和安全。 -
AbstractExecutorService:實際上,Java標準庫中沒有名為
AbstractExecutorService
的接口。可能您是想引用一個抽象類,但ExecutorService
接口本身通常被具體類(如ThreadPoolExecutor
)實現,而不是由抽象類來擴展。在標準庫中,有一個AbstractExecutorService
類的可能性很低,但如果有這樣的類,它可能提供了一些ExecutorService
接口的默認實現。 -
ThreadPoolExecutor:這是一個實現了
ExecutorService
接口的具體類,它提供了線程池的實現,允許用戶配置核心線程數、最大線程數、線程空閑時間、任務隊列等參數。ThreadPoolExecutor
是線程池框架中最常用的實現之一,它高效地管理線程資源的復用,降低了系統的開銷。
ThreadPoolExecutor 構造器
java.uitl.concurrent.ThreadPoolExecutor
類是線程池中最核心的一個類,因此如果要透徹地了解Java中的線程池,必須先了解這個類。下面我們來看一下ThreadPoolExecutor
類的具體實現源碼。
public class ThreadPoolExecutor extends AbstractExecutorService {public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue);public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
...
}
從上面的代碼可以得知,ThreadPoolExecutor繼承了AbstractExecutorService類,并提供了四個構造器,事實上,通過觀察每個構造器的源碼具體實現,發現前面三個構造器都是調用的第四個構造器進行的初始化工作。
構造器參數
了解如何配置線程池。這包括設置核心線程數、最大線程數、隊列容量等參數。這些參數的選擇將直接影響線程池的性能和穩定性。通過合理地配置這些參數,我們可以在保證系統性能的同時,也避免了資源的過度消耗。
構造器中各個參數的含義
corePoolSize
corePoolSize
是線程池中的一個關鍵參數,它決定了線程池中的核心線程數量。
在創建線程池后,初始狀態下線程池內部并不包含任何線程。線程池會等待任務的到來,并根據需要創建線程來執行任務。除非顯式地調用 prestartAllCoreThreads()
或 prestartCoreThread()
方法,線程池才會預先創建核心線程。從這兩個方法的名字就可以推斷出,它們的目的是在未接收到任務之前就創建指定數量的線程。
在默認情況下,當第一個任務到達時,線程池會創建一個新線程來執行它。隨著更多任務的到達,線程池中的線程數量會逐漸增加,直到達到 corePoolSize
所設定的數量。一旦線程池中的線程數量達到這個核心值,后續到達的任務將不再觸發新線程的創建。
此時,如果還有新任務到來,線程池會將它們放入一個緩存隊列中等待執行。這個隊列通常是一個阻塞隊列,用于存儲待執行的任務。只有當隊列滿了,或者線程池中的線程數量低于 maximumPoolSize
(最大線程池大小)時,線程池才會考慮創建額外的線程來執行任務。
注意:合理配置
corePoolSize
,可以平衡線程創建和銷毀的開銷與任務執行的效率。如果corePoolSize
設置得太小,可能會導致大量任務在隊列中等待,從而增加任務的延遲;如果設置得太大,又可能浪費系統資源,因為不是所有線程都會同時處于忙碌狀態。因此,在實際應用中,需要根據任務的特性和系統資源來合理設置這個參數。
maximumPoolSize
線程池中的maximumPoolSize參數指的是線程池能夠容納的最大線程數量。當線程池中的線程數量達到這個值時,新的任務提交到線程池時,線程池將不會再創建新的線程來處理這些任務,而是根據線程池的其他策略(如隊列策略)來決定如何處理這些新任務。
keepAliveTime
線程沒有任務執行時最多保持多久時間會終止。
默認情況下,只有當線程池中的線程數大于corePoolSize時,keepAliveTime才會起作用,直到線程池中的線程數不大于corePoolSize,即當線程池中的線程數大于corePoolSize時,如果一個線程空閑的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。
注意:如果調用了
allowCoreThreadTimeOut(boolean)
方法,在線程池中的線程數不大于corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數為0;
unit
unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性:
workQueue
一個阻塞隊列,用來存儲等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程產生重大影響,一般來說,這里的阻塞隊列有以下幾種選擇:
線程池中的阻塞隊列(BlockingQueue)是線程池的一個重要組成部分,用于存儲待執行的任務。當線程池中的線程數量達到corePoolSize
時,新提交的任務會被放入這個隊列中等待執行。阻塞隊列的類型會影響線程池的行為和性能。以下是您提到的幾種阻塞隊列的作用和特性:
-
ArrayBlockingQueue(數組類型隊列)
- 特點:基于數組結構的有界阻塞隊列。
- 容量限制:在創建時需要指定隊列的容量,一旦隊列滿了,新提交的任務會被拒絕。
- 性能:由于是基于數組,它在入隊和出隊操作上的時間復雜度是O(1),具有較好的性能。
- 使用場景:適用于有固定大小的任務緩存場景,當任務量比較大時,能夠避免在內存中創建大量的對象。
-
LinkedBlockingQueue(鏈表類型隊列)
- 特點:基于鏈表結構的有界(但默認大小為Integer.MAX_VALUE,可視為無界)阻塞隊列。
- 容量限制:可以指定隊列的容量,但如果不指定,則默認為Integer的最大值,幾乎可以認為是無界的。
- 性能:入隊和出隊操作的時間復雜度為O(1),但由于是鏈表結構,實際性能可能會略低于ArrayBlockingQueue。
- 使用場景:適用于任務量較大,但不想或不需要限制隊列大小的場景。
-
SynchronousQueue(同步單元素隊列)
- 特點:一個不存儲元素的阻塞隊列,也就是說它的容量為1。
- 容量限制:只能存儲一個元素,如果隊列中有元素,則新提交的任務可以直接獲取執行,否則需要等待其他線程執行完任務后,騰出空間才能繼續。
- 性能:入隊和出隊操作的時間復雜度接近O(1),但由于其特殊的特性,線程間的交互更為頻繁,可能導致更高的線程調度開銷。
- 使用場景:適用于線程池中的線程數量與任務數量大致相等,且任務執行時間較短的場景。
-
PriorityBlockingQueue(具有優先級的隊列)
- 特點:一個支持優先級排序的無界阻塞隊列。
- 容量限制:默認是無界的,可以存儲任意數量的任務。
- 優先級:隊列中的元素按照它們的自然排序或者通過提供的Comparator進行排序,優先級高的任務將優先被執行。
- 性能:由于需要排序,入隊和出隊操作的時間復雜度可能會高于O(1)。
- 使用場景:適用于需要按照優先級執行任務的場景,例如,某些重要或緊急的任務需要優先得到處理。
ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊策略與BlockingQueue有關。
threadFactory
線程工廠,主要用來創建線程的工廠實現類。
public interface ThreadFactory {/*** Constructs a new {@code Thread}. Implementations may also initialize* priority, name, daemon status, {@code ThreadGroup}, etc.** @param r a runnable to be executed by new thread instance* @return constructed thread, or {@code null} if the request to* create a thread is rejected*/Thread newThread(Runnable r);
}
handler
表示當拒絕處理任務時的策略,有以下四種取值: