Cả hai là ExecutorService do Executors tạo ra, dựa trên ThreadPoolExecutor nhưng cấu hình queue và thread lifecycle khác biệt.
Bảng so sánh
| Tiêu chí | Fixed Thread Pool | Cached Thread Pool |
|---|---|---|
| Factory method | Executors.newFixedThreadPool(nThreads) | Executors.newCachedThreadPool() |
| ThreadPoolExecutor config | core=max=nThreads, keepAlive=0, queue=LinkedBlockingQueue (unbounded) | core=0, max=Integer.MAX_VALUE, keepAlive=60s, queue=SynchronousQueue |
| Queue type | Unbounded BlockingQueue (LinkedBlockingQueue) | Direct handoff queue (SynchronousQueue – capacity 0) |
| Thread creation | Tạo sẵn nThreads khi submit task đầu tiên. Giữ cố định. | Tạo thread mới khi cần (queue full – luôn full vì SynchronousQueue). Reuse idle thread. |
| Thread termination | Không tự terminate (keepAlive=0). Chỉ chết khi shutdown(). | Thread idle > 60s → terminate. Pool co lại về 0. |
| Pool size behavior | Cố định (fixed) | Động: 0 → Integer.MAX_VALUE |
| Backpressure | Queue unbounded → task chờ vô hạn → OOM risk nếu producer > consumer. | Không queue (handoff) → task bị reject nếu không tạo thread kịp (hoặc tạo thread mới). |
| Use case | Giới hạn concurrency, workload ổn định, long-running tasks, server handling requests. | Nhiều short-lived tasks, bursty workload, async event handling, parallel independent tasks. |
| Rủi ro | Queue unbounded → OOM. Thread leak nếu task block forever. | Tạo thread không giới hạn → OOM, thread thrashing, CPU context switch overhead. |
Ví dụ Fixed Thread Pool
import java.util.concurrent.*;
public class FixedThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(3);
CountDownLatch latch = new CountDownLatch(5);
for (int i = 1; i <= 5; i++) {
final int id = i;
executor.submit(() -> {
try {
System.out.println("Task " + id + " running on " + Thread.currentThread().getName());
Thread.sleep(1000);
} catch (InterruptedException e) { Thread.currentThread().interrupt(); }
finally { latch.countDown(); }
});
}
latch.await();
System.out.println("All tasks done. Active threads: " + ((ThreadPoolExecutor)executor).getActiveCount());
executor.shutdown();
}
}
Ví dụ Cached Thread Pool
import java.util.concurrent.*;
public class CachedThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(10);
// Submit 10 short tasks nhanh chóng
for (int i = 1; i <= 10; i++) {
final int id = i;
executor.submit(() -> {
System.out.println("Task " + id + " on " + Thread.currentThread().getName());
try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
latch.countDown();
});
}
latch.await();
System.out.println("Pool size after: " + ((ThreadPoolExecutor)executor).getPoolSize()); // Sẽ giảm dần về 0
Thread.sleep(70000); // Chờ 70s > keepAlive 60s
System.out.println("Pool size after 70s idle: " + ((ThreadPoolExecutor)executor).getPoolSize()); // 0
executor.shutdown();
}
}
Best Practice: Tự custom ThreadPoolExecutor (Production)
// Fixed pool với bounded queue – an toàn hơn
ExecutorService fixed = new ThreadPoolExecutor(
10, 10, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(100), // Bounded queue!
new ThreadFactoryBuilder().setNameFormat("fixed-worker-%d").build(),
new ThreadPoolExecutor.AbortPolicy() // Hoặc CallerRunsPolicy
);
// Cached pool với giới hạn max threads
ExecutorService cached = new ThreadPoolExecutor(
0, 50, 60L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
new ThreadFactoryBuilder().setNameFormat("cached-worker-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy() // Backpressure: caller chạy task nếu pool full
);
Tóm tắt chọn lựa
- Fixed Thread Pool: Server xử lý request, worker pool ổn định, cần kiểm soát resource.
- Cached Thread Pool: Batch processing, event-driven, short tasks, burst traffic – nhưng cẩn thận unbounded thread creation.
- Production: Luôn custom ThreadPoolExecutor với bounded queue, named threads, rejection policy, monitoring.