問題背景
Thread 不是免費的——創建一個 thread 需要分配 stack memory(預設 1MB)、OS kernel 層的資源、和 context switch 的開銷。如果每個任務都建新 thread:
- 高並發時系統同時有幾千個 thread,記憶體消耗巨大
- Thread 創建和銷毀的時間成本累積成 latency
- 沒有流量控制——任務洪水時系統崩潰
Thread Pool 解法:預先建立 N 個 worker thread,任務進入 queue,worker 從 queue 取任務執行,執行完回到池中等待下一個任務。
核心元件
Task Queue(任務佇列)
↓
[Worker 1] [Worker 2] ... [Worker N] ← Thread Pool
↓
Task Result
關鍵參數:
- core pool size:常駐 thread 數
- max pool size:最多可以有多少 thread(超過 core size 時動態擴充)
- queue capacity:任務佇列大小
- rejection policy:queue 滿了且已到 max pool size 時怎麼辦
Java:ExecutorService
import java.util.concurrent.*;
// Fixed thread pool:固定 8 個 worker
ExecutorService pool = Executors.newFixedThreadPool(8);
// 或自定義 ThreadPoolExecutor
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // corePoolSize
16, // maximumPoolSize
60, TimeUnit.SECONDS, // keepAlive(idle thread 存活時間)
new LinkedBlockingQueue<>(1000),// work queue(容量 1000)
new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);
// 提交任務
executor.submit(() -> {
// 任務邏輯
return "result";
});
// 關閉 pool(等待已提交的任務完成)
executor.shutdown();
executor.awaitTermination(30, TimeUnit.SECONDS);Rejection Policy 選項:
AbortPolicy(預設):丟RejectedExecutionExceptionCallerRunsPolicy:提交者自己執行任務(backpressure)DiscardPolicy:靜默丟棄DiscardOldestPolicy:丟掉最舊的等待任務
Go:Goroutine Pool
Go 的 goroutine 比 OS thread 輕量(初始 2KB stack),但仍然需要控制並發度:
func NewWorkerPool(workerCount int, jobQueue chan func()) {
for i := 0; i < workerCount; i++ {
go func() {
for job := range jobQueue {
job()
}
}()
}
}
// 使用
jobs := make(chan func(), 1000)
NewWorkerPool(8, jobs)
// 提交任務
jobs <- func() {
fmt.Println("processing")
}Go 的 semaphore 包或 errgroup 也可以限制並發數:
import "golang.org/x/sync/semaphore"
sem := semaphore.NewWeighted(8) // 最多 8 個並發
for _, item := range items {
sem.Acquire(ctx, 1)
go func(item Item) {
defer sem.Release(1)
process(item)
}(item)
}適用場景
- Web server 的 request 處理(Spring Boot 的 Tomcat 用 thread pool)
- 批次任務處理(大量 email 發送、圖片縮圖生成)
- I/O 密集型任務並行化(爬蟲、API aggregation)
不適合:CPU 密集型任務的 pool size 超過 CPU 核心數(反而因 context switch 更慢)。CPU 密集型任務的最佳 pool size 通常是 CPU 核心數 或 CPU 核心數 + 1。