執行緒池是一種管理和複用執行緒資源的機制,它由一個執行緒池管理器和一組工作執行緒組成。執行緒池管理器負責建立和銷燬執行緒池,以及管理執行緒池中的工作執行緒。工作執行緒則負責執行具體的任務。
執行緒池的主要作用是管理和複用執行緒資源,避免了執行緒的頻繁建立和銷燬所帶來的開銷。
執行緒池包含兩個重要的組成部分:
執行緒池大小:指執行緒池中所能容納的最大執行緒數。執行緒池大小一般根據系統的負載情況和硬體資源來設定。
工作佇列:用於存放等待執行的任務。當執行緒池中的工作執行緒已經全部被佔用時,新的任務將被加入到工作佇列中等待執行。
Java中執行緒池的建立方式主要有以下幾種:
使用 ThreadPoolExecutor 類手動建立:通過 ThreadPoolExecutor 類別建構函式自定義執行緒池的引數,包括核心執行緒數、最大執行緒數、執行緒存活時間、任務佇列等。
使用 Executors 類提供的工廠方法建立:通過 Executors 類提供的一些靜態工廠方法建立執行緒池,例如 newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool 等。
使用 Spring 框架提供的 ThreadPoolTaskExecutor 類:在 Spring 框架中可以通過 ThreadPoolTaskExecutor 類來建立執行緒池。
不同的建立方式適用於不同的場景,通常可以根據實際情況選擇合適的方式建立執行緒池。手動建立 ThreadPoolExecutor 類可以靈活地設定執行緒池引數,但需要對執行緒池的各項引數有一定的瞭解;使用 Executors 工廠方法可以快速建立執行緒池,但可能無法滿足特定的需求,且容易出現記憶體溢位的情況;而 Spring 框架提供的 ThreadPoolTaskExecutor 類則只能在 Spring 框架中使用。
ThreadPoolExecutor 執行緒的建立與使用範例如下:
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 建立執行緒池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心執行緒數
5, // 最大執行緒數
60, // 執行緒池中執行緒的空閒時間(單位為秒)
TimeUnit.SECONDS, // 時間單位
new ArrayBlockingQueue<>(10) // 任務佇列
);
// 提交任務
for (int i = 1; i <= 20; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("執行任務:" + taskId + ",執行緒名稱:" + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模擬任務執行時間
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 關閉執行緒池
executor.shutdown();
}
}
在上面的範例中,我們通過 ThreadPoolExecutor 類手動建立了一個執行緒池,設定了執行緒池的核心執行緒數為 2,最大執行緒數為 5,執行緒空閒時間為 60 秒,任務佇列為長度為 10 的 ArrayBlockingQueue。然後我們通過 submit() 方法向執行緒池中提交了 20 個任務,每個任務會在執行時輸出自己的編號和執行執行緒的名稱,然後睡眠1秒鐘模擬任務執行時間。最後,我們呼叫 shutdown() 方法關閉執行緒池。
import java.util.concurrent.*;
public class ExecutorsDemo {
public static void main(String[] args) {
// 建立執行緒池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任務
for (int i = 1; i <= 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("執行任務:" + taskId + ",執行緒名稱:" + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模擬任務執行時間
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 關閉執行緒池
executor.shutdown();
}
}
在上面的範例中,我們使用了 Executors 工廠類中的 newFixedThreadPool() 方法建立了一個執行緒池,該執行緒池有 5 個固定執行緒。然後我們通過 submit() 方法向執行緒池中提交了 10 個任務,每個任務會在執行時輸出自己的編號和執行執行緒的名稱,然後睡眠 1 秒鐘模擬任務執行時間。最後,我們呼叫 shutdown() 方法關閉執行緒池。值得注意的是,使用 Executors 工廠類建立執行緒池雖然非常簡單,但是在實際生產環境中並不推薦,因為這種方式很容易導致執行緒資源被耗盡,從而影響系統的效能和穩定性。
Spring 中 ThreadPoolTaskExecutor 的使用範例如下:
import java.util.concurrent.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
public class ThreadPoolTaskExecutorDemo {
public static void main(String[] args) {
// 建立執行緒池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2); // 核心執行緒數
executor.setMaxPoolSize(5); // 最大執行緒數
executor.setQueueCapacity(10); // 任務佇列長度
executor.initialize();
// 提交任務
for (int i = 1; i <= 20; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("執行任務:" + taskId + ",執行緒名稱:" + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模擬任務執行時間
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 關閉執行緒池
executor.shutdown();
}
}
在上面的範例中,我們使用了 Spring 框架中的 ThreadPoolTaskExecutor 類建立了一個執行緒池,設定了執行緒池的核心執行緒數為 2,最大執行緒數為 5,任務佇列長度為 10。然後我們通過 submit() 方法向執行緒池中提交了 20 個任務,每個任務會在執行時輸出自己的編號和執行執行緒的名稱,然後睡眠1秒鐘模擬任務執行時間。最後,我們呼叫 shutdown() 方法關閉執行緒池。注意,在使用 Spring 框架中的 ThreadPoolTaskExecutor 類建立執行緒池時,我們需要先呼叫 initialize() 方法進行初始化。
ThreadPoolTaskExecutor 底層還是通過 JDK 中提供的 ThreadPoolExecutor 類實現的。
執行緒池相比於執行緒來說,它不需要頻繁的建立和銷燬執行緒,執行緒一旦建立之後,預設情況下就會一直保持線上程池中,等到有任務來了,再用這些已有的執行緒來執行任務,如下圖所示:
執行緒在建立時要開闢虛擬機器器棧、本地方法棧、程式計數器等私有執行緒的記憶體空間,而銷燬時又要回收這些私有空間資源,如下圖所示:
而執行緒池建立了執行緒之後就會放線上程池中,因此執行緒池相比於執行緒來說,第一個優點就是可以複用執行緒、減低系統資源的消耗。
執行緒池是複用已有執行緒來執行任務的,而執行緒是在有任務時才新建的,所以相比於執行緒來說,執行緒池能夠更快的響應任務和執行任務。
執行緒池提供了更多的管理功能,這裡管理功能主要體現在以下兩個方面:
執行緒池相比於執行緒來說提供了更多的功能,比如定時執行和週期執行等功能。
執行緒池是一種管理和複用執行緒資源的機制。相比於執行緒,它具備四個主要優勢:1.複用執行緒,降低了資源消耗;2.提高響應速度;3.提供了管理執行緒數和任務數的能力;4.更多增強功能。
本文已收錄至《Java面試突擊》,專注 Java 面試 100 年,檢視更多:www.javacn.site