線程池的工作原理及其在業(yè)務(wù)中的實踐
簡介
什么是線程池?
線程池是一種用于管理和復(fù)用線程的機制。
線程池的核心思想是預(yù)先創(chuàng)建一定數(shù)量的線程,并把它們保存在線程池中,當(dāng)有任務(wù)需要執(zhí)行時,線程池會從空閑線程中取出一個線程來執(zhí)行該任務(wù)。任務(wù)執(zhí)行完畢后,線程不是被銷毀,而是返還給線程池,可以立即或稍后被再次用來執(zhí)行其他任務(wù)。這種機制可以避免因頻繁創(chuàng)建和銷毀線程而帶來的性能開銷,同時也能控制同時運行的線程數(shù)量,從而提高系統(tǒng)的性能和資源利用率。
線程池的主要組成部分包括工作線程、任務(wù)隊列、線程管理器等。線程池的設(shè)計有助于優(yōu)化多線程程序的性能和資源利用,同時簡化了線程的管理和復(fù)用的復(fù)雜性。
線程池有什么好處?
- 減少線程創(chuàng)建和銷毀的開銷,線程的創(chuàng)建和銷毀需要消耗系統(tǒng)資源,線程池通過復(fù)用線程,避免了對資源的頻繁操作,從而提高系統(tǒng)性能;
- 控制和優(yōu)化系統(tǒng)資源利用,線程池通過控制線程的數(shù)量,可以盡可能地壓榨機器性能,提高系統(tǒng)資源利用率;
- 提高響應(yīng)速度,線程池可以預(yù)先創(chuàng)建線程且通過多線程并發(fā)處理任務(wù),提升任務(wù)的響應(yīng)速度及系統(tǒng)的并發(fā)性能;
線程池狀態(tài)
RUNNING
:線程池一旦被創(chuàng)建,就處于RUNNING
狀態(tài),任務(wù)數(shù)為0
,能夠接收新任務(wù),對已排隊的任務(wù)進行處理。SHUTDOWN
:不接收新任務(wù),但能處理已排隊的任務(wù)。當(dāng)調(diào)用線程池的shutdown()
方法時,線程池會由RUNNING
轉(zhuǎn)變?yōu)?/span>SHUTDOWN
狀態(tài)。STOP
:不接收新任務(wù),不處理已排隊的任務(wù),并且會中斷正在處理的任務(wù)。當(dāng)調(diào)用線程池的shutdownNow()
方法時,線程池會由RUNNING
或SHUTDOWN
轉(zhuǎn)變?yōu)?/span>STOP
狀態(tài)。TIDYING
:當(dāng)線程池在SHUTDOWN
狀態(tài)下,任務(wù)隊列為空且執(zhí)行中任務(wù)為空,或者線程池在STOP
狀態(tài)下,線程池中執(zhí)行中任務(wù)為空時,線程池會變?yōu)?/span>TIDYING
狀態(tài),會執(zhí)行terminated()
方法。這個方法在線程池中是空實現(xiàn),可以重寫該方法進行相應(yīng)的處理。TERMINATED
:線程池徹底終止。線程池在TIDYING
狀態(tài)執(zhí)行完terminated()
方法后,就會由TIDYING
轉(zhuǎn)變?yōu)?/span>TERMINATED
狀態(tài)。
線程狀態(tài)
- 初始(
NEW
):新創(chuàng)建了一個線程對象,但還沒有調(diào)用start()
方法。 - 運行(
RUNNABLE
):Java
線程中將就緒(READY
)和運行中(RUNNING
)兩種狀態(tài)籠統(tǒng)的稱為“運行”。
a.就緒(READY
):線程對象創(chuàng)建后,其他線程(比如main
線程)調(diào)用了該對象的start()
方法。該狀態(tài)的線程位于可運行線程池中,等待被線程調(diào)度選中并分配cpu
使用權(quán) 。
b.運行中(RUNNING
):就緒(READY
)的線程獲得了cpu
時間片,開始執(zhí)行程序代碼。
- 阻塞(
BLOCKED
):表示線程阻塞于鎖。 - 等待(
WAITING
):進入該狀態(tài)的線程需要等待其他線程做出一些特定動作(通知或中斷)。 - 超時等待(
TIMED_WAITING
):該狀態(tài)不同于WAITING
,它可以在指定的時間后自行返回。 - 終止(
TERMINATED
):表示該線程已經(jīng)執(zhí)行完畢。
拒絕策略
線程池的拒絕策略決定了在任務(wù)隊列已滿的情況下,如何處理新提交的任務(wù)。
AbortPolicy
- 這是默認(rèn)的拒絕策略,當(dāng)線程池?zé)o法接受新任務(wù)時,會拋出RejectedExecutionException
異常。這意味著新任務(wù)會被立即拒絕,不會加入到任務(wù)隊列中,也不會執(zhí)行。通常情況下都是使用這種拒絕策略。DiscardPolicy
- 這個策略在任務(wù)隊列已滿時,會丟棄新的任務(wù)而且不會拋出異常。新任務(wù)提交后會被默默地丟棄,不會有任何提示或執(zhí)行。這個策略一般用于日志記錄、統(tǒng)計等不是非常關(guān)鍵的任務(wù)。DiscardOldestPolicy
- 這個策略也會丟棄任務(wù),但它會先嘗試將任務(wù)隊列中最早的任務(wù)刪除,然后再嘗試提交新任務(wù)。如果任務(wù)隊列已滿,且線程池中的線程都在工作,可能會導(dǎo)致一些任務(wù)被丟棄。這個策略對于一些實時性要求較高的場景比較合適。CallerRunsPolicy
- 這個策略將任務(wù)回退給調(diào)用線程,而不會拋出異常。調(diào)用線程會嘗試執(zhí)行任務(wù)。這個策略可以降低任務(wù)提交速度,適用于任務(wù)提交者能夠承受任務(wù)執(zhí)行的壓力,但希望有一種緩沖機制的情況。
?
一般來說,默認(rèn)的拒絕策略還是比較常用的,因為大多數(shù)情況下我們不太會讓任務(wù)多到線程池中放不下,要不然就提升執(zhí)行速度,要不然就提升隊列長度了。
工作原理
- 任務(wù)提交:當(dāng)有新任務(wù)提交到線程池時,線程池會根據(jù)當(dāng)前狀態(tài)決定如何處理該任務(wù)。
- 核心線程處理:如果當(dāng)前運行的線程數(shù)少于核心線程數(shù)(
corePoolSize
),線程池會立即創(chuàng)建一個新線程來執(zhí)行任務(wù),即使其他核心線程處于空閑狀態(tài)。 - 任務(wù)隊列緩沖:如果當(dāng)前運行的線程數(shù)等于或大于核心線程數(shù),新任務(wù)會被放入任務(wù)隊列(
workQueue
)中等待執(zhí)行。 - 最大線程處理:如果任務(wù)隊列已滿且運行的線程數(shù)少于最大線程數(shù)(
maximumPoolSize
),線程池會創(chuàng)建新的線程來處理任務(wù)。 - 拒絕策略執(zhí)行:如果任務(wù)隊列已滿且運行的線程數(shù)等于最大線程數(shù),線程池會執(zhí)行拒絕策略(
RejectedExecutionHandler
)來處理新提交的任務(wù)。 - 線程回收:當(dāng)線程完成任務(wù)后,如果空閑時間超過
keepAliveTime
,非核心線程會被回收,以減少資源消耗。
如何使用
創(chuàng)建線程池
public class ThreadPoolUtils {
/**
* 線程池
*/
private static ExecutorService executor = initDefaultExecutor();
/**
* 統(tǒng)一的獲取線程池對象方法
*/
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, // 核心線程數(shù)
DEFAULT_THREAD_SIZE, // 最大線程數(shù)
300, TimeUnit.SECONDS, // 線程空閑時間
new ArrayBlockingQueue<>(DEFAULT_QUEUE_SIZE), // 任務(wù)隊列
new DefaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()); // 拒絕策略
}
}
創(chuàng)建一個簡單的任務(wù)類,并將其提交到線程池中執(zhí)行:
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 {
// 模擬任務(wù)執(zhí)行時間
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();
// 提交任務(wù)到線程池
for (int i = 1; i <= 20; i++) {
executor.submit(new MyTask(i));
}
// 關(guān)閉線程池
executor.shutdown();
try {
// 等待所有任務(wù)執(zhí)行完畢
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
// 如果等待超時,中斷線程池中的線程
executor.shutdownNow();
}
} catch (InterruptedException e) {
// 如果在等待過程中被中斷,中斷線程池中的線程
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}