問題背景

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(預設):丟 RejectedExecutionException
  • CallerRunsPolicy:提交者自己執行任務(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