線程池的工作原理及其在業務中的實踐

簡介
什么是線程池?
線程池是一種用于管理和復用線程的機制。
線程池的核心思想是預先創建一定數量的線程,并把它們保存在線程池中,當有任務需要執行時,線程池會從空閑線程中取出一個線程來執行該任務。任務執行完畢后,線程不是被銷毀,而是返還給線程池,可以立即或稍后被再次用來執行其他任務。這種機制可以避免因頻繁創建和銷毀線程而帶來的性能開銷,同時也能控制同時運行的線程數量,從而提高系統的性能和資源利用率。
線程池的主要組成部分包括工作線程、任務隊列、線程管理器等。線程池的設計有助于優化多線程程序的性能和資源利用,同時簡化了線程的管理和復用的復雜性。
線程池有什么好處?
- 減少線程創建和銷毀的開銷,線程的創建和銷毀需要消耗系統資源,線程池通過復用線程,避免了對資源的頻繁操作,從而提高系統性能;
- 控制和優化系統資源利用,線程池通過控制線程的數量,可以盡可能地壓榨機器性能,提高系統資源利用率;
- 提高響應速度,線程池可以預先創建線程且通過多線程并發處理任務,提升任務的響應速度及系統的并發性能;
線程池狀態
RUNNING:線程池一旦被創建,就處于RUNNING狀態,任務數為0,能夠接收新任務,對已排隊的任務進行處理。SHUTDOWN:不接收新任務,但能處理已排隊的任務。當調用線程池的shutdown()方法時,線程池會由RUNNING轉變為SHUTDOWN狀態。STOP:不接收新任務,不處理已排隊的任務,并且會中斷正在處理的任務。當調用線程池的shutdownNow()方法時,線程池會由RUNNING或SHUTDOWN轉變為STOP狀態。TIDYING:當線程池在SHUTDOWN狀態下,任務隊列為空且執行中任務為空,或者線程池在STOP狀態下,線程池中執行中任務為空時,線程池會變為TIDYING狀態,會執行terminated()方法。這個方法在線程池中是空實現,可以重寫該方法進行相應的處理。TERMINATED:線程池徹底終止。線程池在TIDYING狀態執行完terminated()方法后,就會由TIDYING轉變為TERMINATED狀態。
線程狀態
- 初始(
NEW):新創建了一個線程對象,但還沒有調用start()方法。 - 運行(
RUNNABLE):Java線程中將就緒(READY)和運行中(RUNNING)兩種狀態籠統的稱為“運行”。
a.就緒(READY):線程對象創建后,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位于可運行線程池中,等待被線程調度選中并分配cpu使用權 。
b.運行中(RUNNING):就緒(READY)的線程獲得了cpu時間片,開始執行程序代碼。
- 阻塞(
BLOCKED):表示線程阻塞于鎖。 - 等待(
WAITING):進入該狀態的線程需要等待其他線程做出一些特定動作(通知或中斷)。 - 超時等待(
TIMED_WAITING):該狀態不同于WAITING,它可以在指定的時間后自行返回。 - 終止(
TERMINATED):表示該線程已經執行完畢。
拒絕策略
線程池的拒絕策略決定了在任務隊列已滿的情況下,如何處理新提交的任務。
AbortPolicy- 這是默認的拒絕策略,當線程池無法接受新任務時,會拋出RejectedExecutionException異常。這意味著新任務會被立即拒絕,不會加入到任務隊列中,也不會執行。通常情況下都是使用這種拒絕策略。DiscardPolicy- 這個策略在任務隊列已滿時,會丟棄新的任務而且不會拋出異常。新任務提交后會被默默地丟棄,不會有任何提示或執行。這個策略一般用于日志記錄、統計等不是非常關鍵的任務。DiscardOldestPolicy- 這個策略也會丟棄任務,但它會先嘗試將任務隊列中最早的任務刪除,然后再嘗試提交新任務。如果任務隊列已滿,且線程池中的線程都在工作,可能會導致一些任務被丟棄。這個策略對于一些實時性要求較高的場景比較合適。CallerRunsPolicy- 這個策略將任務回退給調用線程,而不會拋出異常。調用線程會嘗試執行任務。這個策略可以降低任務提交速度,適用于任務提交者能夠承受任務執行的壓力,但希望有一種緩沖機制的情況。
?
一般來說,默認的拒絕策略還是比較常用的,因為大多數情況下我們不太會讓任務多到線程池中放不下,要不然就提升執行速度,要不然就提升隊列長度了。
工作原理

- 任務提交:當有新任務提交到線程池時,線程池會根據當前狀態決定如何處理該任務。
- 核心線程處理:如果當前運行的線程數少于核心線程數(
corePoolSize),線程池會立即創建一個新線程來執行任務,即使其他核心線程處于空閑狀態。 - 任務隊列緩沖:如果當前運行的線程數等于或大于核心線程數,新任務會被放入任務隊列(
workQueue)中等待執行。 - 最大線程處理:如果任務隊列已滿且運行的線程數少于最大線程數(
maximumPoolSize),線程池會創建新的線程來處理任務。 - 拒絕策略執行:如果任務隊列已滿且運行的線程數等于最大線程數,線程池會執行拒絕策略(
RejectedExecutionHandler)來處理新提交的任務。 - 線程回收:當線程完成任務后,如果空閑時間超過
keepAliveTime,非核心線程會被回收,以減少資源消耗。
如何使用
創建線程池
public class ThreadPoolUtils {
/**
* 線程池
*/
private static ExecutorService executor = initDefaultExecutor();
/**
* 統一的獲取線程池對象方法
*/
public static ExecutorService getExecutor() {
return executor;
}
private static final int DEFAULT_THREAD_SIZE = 16;
private static final int DEFAULT_QUEUE_SIZE = 10240;
private static ExecutorService initDefaultExecutor() {
return new ThreadPoolExecutor(
DEFAULT_THREAD_SIZE, // 核心線程數
DEFAULT_THREAD_SIZE, // 最大線程數
300, TimeUnit.SECONDS, // 線程空閑時間
new ArrayBlockingQueue<>(DEFAULT_QUEUE_SIZE), // 任務隊列
new DefaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()); // 拒絕策略
}
}創建一個簡單的任務類,并將其提交到線程池中執行:
class MyTask implements Runnable {
private final int taskId;
public MyTask(int taskId) {
this.taskId = taskId;
}
@Override
public void run() {
System.out.println("Task " + taskId + " is being executed by thread " + Thread.currentThread().getName());
try {
// 模擬任務執行時間
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Task " + taskId + " has been completed.");
}
}
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = ThreadPoolUtils.getExecutor();
// 提交任務到線程池
for (int i = 1; i <= 20; i++) {
executor.submit(new MyTask(i));
}
// 關閉線程池
executor.shutdown();
try {
// 等待所有任務執行完畢
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
// 如果等待超時,中斷線程池中的線程
executor.shutdownNow();
}
} catch (InterruptedException e) {
// 如果在等待過程中被中斷,中斷線程池中的線程
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}




























