Việc tạo quá nhiều luồng (thread) có thể gây ra chi phí điều phối lớn, ảnh hưởng đến hiệu suất tổng thể và tính cục bộ của bộ nhớ đệm. Thread pool giúp khắc phục vấn đề này bằng cách khởi tạo sẵn một tập hợp các luồng, sẵn sàng nhận và thực thi tác vụ khi được CPU phân bổ. Sau khi hoàn thành công việc, các luồng này quay trở lại "hồ chứa" để chờ nhiệm vụ tiếp theo — từ đó loại bỏ chi phí khởi tạo và giải phóng luồng cho các tác vụ ngắn hạn.
Thread pool không chỉ tối ưu việc sử dụng tài nguyên hệ thống mà còn đảm bảo tận dụng hiệu quả nhân CPU, đồng thời tránh tình trạng điều phối quá mức.
Các phương thức tạo thread pool phổ biến
Executors.newSingleThreadExecutor(): Tạo một executor chỉ sử dụng duy nhất một luồng, lấy tác vụ từ hàng đợi vô hạn.Executors.newFixedThreadPool(int nThreads): Tạo một pool với số lượng luồng cố định, chia sẻ cùng một hàng đợi vô hạn.Executors.newCachedThreadPool(): Tạo một pool linh hoạt – tự động mở rộng số luồng khi cần và tái sử dụng luồng rảnh nếu có.
public class ExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
try {
for (int i = 1; i <= 15; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " xử lý tác vụ " + taskId);
});
}
} finally {
executor.shutdown();
}
}
}
Bảy tham số cấu hình ThreadPoolExecutor
- corePoolSize: Số luồng cốt lõi luôn được giữ lại, ngay cả khi rảnh.
- maximumPoolSize: Số luồng tối đa cho phép trong pool.
- keepAliveTime: Thời gian chờ trước khi các luồng vượt quá corePoolSize bị hủy nếu không có việc.
- unit: Đơn vị thời gian cho keepAliveTime.
- workQueue: Hàng đợi chặn (BlockingQueue) lưu trữ các tác vụ đang chờ xử lý.
- threadFactory: Nhà máy tạo luồng — thường dùng mặc định.
- handler: Chính sách xử lý khi tác vụ bị từ chối (khi hàng đợi đầy và số luồng đạt maximumPoolSize).
Các chính sách từ chối phổ biến:
AbortPolicy: NémRejectedExecutionException.CallerRunsPolicy: Thực thi tác vụ ngay trên luồng gọiexecute().DiscardOldestPolicy: Loại bỏ tác vụ cũ nhất trong hàng đợi và thử lại.DiscardPolicy: Bỏ qua tác vụ bị từ chối mà không thông báo.
public class CustomThreadPool {
public static void main(String[] args) {
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService executor = new ThreadPoolExecutor(
2, // corePoolSize
6, // maximumPoolSize
3L, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3), // hàng đợi có giới hạn
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
try {
for (int i = 1; i <= 15; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " xử lý #" + taskId);
});
}
} finally {
executor.shutdown();
}
}
}
Xác định kích thước tối ưu cho thread pool
Số nhân CPU có thể lấy bằng:
int cores = Runtime.getRuntime().availableProcessors();
- Tác vụ CPU-bound (tính toán nặng): Số luồng tối ưu ≈
coreshoặccores + 1. - Tác vụ I/O-bound (chờ I/O nhiều): Số luồng tối ưu ≈
2 * coreshoặc cao hơn, tùy vào độ trễ I/O.