面試官:創建線程池的幾種方式以及區別
創建線程池有多種方式,主要通過 Java 的 java.util.concurrent 包提供的 Executors 工具類來實現。以下是幾種常見的線程池類型及其區別:
1. FixedThreadPool
//創建一個固定大小的線程池,模擬提交 10 個任務到線程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolExample {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); // 創建一個具有3個線程的固定線程池
for (int i = 1; i <= 10; i++) {
final int task = i;
fixedThreadPool.execute(() -> {
System.out.println("執行任務 " + task + ",線程:" + Thread.currentThread().getName());
});
}
fixedThreadPool.shutdown();
}
}
- 特點:創建一個固定大小的線程池,池中始終保持指定數量的線程。
- 適用場景:適用于固定并發數的任務,比如定量的短期并發任務。
- 優點:能夠有效地控制線程數量,避免資源消耗過多。
- 缺點:如果所有線程都在執行任務,而新的任務不斷提交,可能會造成等待隊列過長。
2. CachedThreadPool
//創建一個緩存線程池來處理任務,模擬并發執行 10 個任務
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolExample {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 1; i <= 10; i++) {
final int task = i;
cachedThreadPool.execute(() -> {
System.out.println("執行任務 " + task + ",線程:" + Thread.currentThread().getName());
});
}
cachedThreadPool.shutdown();
}
}
- 特點:創建一個可以根據需要自動擴展的線程池,當線程空閑 60 秒后會被回收。
- 適用場景:適合執行大量耗時較短的異步任務。
- 優點:線程數量不受限制(受系統資源限制),對于任務短小、并發量大但不穩定的場景效果較好。
- 缺點:如果任務增長過快,會創建大量線程,可能會造成 OOM(Out of Memory)異常。
3. SingleThreadExecutor
//創建一個單線程線程池,順序執行多個任務。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutorExample {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 1; i <= 5; i++) {
final int task = i;
singleThreadExecutor.execute(() -> {
System.out.println("執行任務 " + task + ",線程:" + Thread.currentThread().getName());
});
}
singleThreadExecutor.shutdown();
}
}
- 特點:創建單線程化的線程池,始終只有一個工作線程。
- 適用場景:適用于需要保證任務順序執行的場景,避免多線程并發的復雜性。
- 優點:可以保證任務按順序執行,適合單一任務隊列。
- 缺點:性能較低,不適合需要高并發的場景。
4. ScheduledThreadPool
//創建一個支持定時和周期性執行任務的線程池,示例任務每隔 2 秒執行一次,共執行 3 次。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolExample {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2); // 創建一個有2個線程的定時線程池
scheduledThreadPool.scheduleAtFixedRate(() -> {
System.out.println("定時任務執行,線程:" + Thread.currentThread().getName());
}, 0, 2, TimeUnit.SECONDS); // 0秒延遲后開始,每隔2秒執行一次任務
// 程序運行5秒后關閉線程池
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
scheduledThreadPool.shutdown();
}
}
- 特點:創建一個支持定時或周期性任務執行的線程池。
- 適用場景:適合執行定時任務或周期性任務,比如定時器、定時檢查等。
- 優點:可以方便地實現周期性任務管理。
- 缺點:對高并發任務的處理能力較弱,通常用于任務量不大的場景。
5. WorkStealingPool(Java 8 引入)
//創建一個基于任務分解的線程池來并行執行多個任務,適合處理需要拆分的小任務。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class WorkStealingPoolExample {
public static void main(String[] args) throws InterruptedException {
ExecutorService workStealingPool = Executors.newWorkStealingPool(); // 創建默認線程數為CPU核心數的工作竊取線程池
for (int i = 1; i <= 8; i++) {
final int task = i;
workStealingPool.submit(() -> {
System.out.println("執行任務 " + task + ",線程:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 讓主線程等待子任務執行完成
workStealingPool.awaitTermination(3, TimeUnit.SECONDS);
workStealingPool.shutdown();
}
}
- 特點:基于 ForkJoinPool 實現,適用于大任務拆分成小任務的并行處理。線程數默認為處理器核心數。
- 適用場景:適合處理較為復雜的并行任務,比如分治算法。
- 優點:通過“工作竊取”算法實現任務的動態負載均衡,能夠有效提升多核 CPU 的利用率。
- 缺點:由于線程數不固定,可能對資源使用較多,不適合所有應用。
區別總結
線程池類型 | 線程數量控制 | 特點 | 適用場景 |
FixedThreadPool | 固定數量 | 固定線程數,適合穩定的任務并發 | 固定并發任務 |
CachedThreadPool | 自動擴展 | 動態擴展,空閑線程自動回收,適合任務短小但并發量不穩定 | 短期的異步并發任務 |
SingleThreadExecutor | 單一線程 | 單線程順序執行任務,保證順序 | 順序執行的任務 |
ScheduledThreadPool | 可控核心線程數 | 支持定時或周期性任務 | 定時任務、周期性任務 |
WorkStealingPool | 默認 CPU 核數 | 基于任務拆分并行處理,提高多核 CPU 利用率 |