Cả hai đều là synchronization aids (công cụ đồng bộ) trong package java.util.concurrent, nhưng phục vụ mục đích khác nhau.
Bản chất cốt lõi
- CountDownLatch: "Đếm ngược" – một hoặc nhiều thread chờ cho đến khi một tập hợp tác vụ (tasks) hoàn thành. One-shot (dùng một lần).
- CyclicBarrier: "Rào chắn vòng" – một tập hợp thread chờ nhau tại một điểm gặp (barrier point) chung. Reusable (tái sử dụng được).
Bảng so sánh chi tiết
| Tiêu chí | CountDownLatch | CyclicBarrier |
|---|---|---|
| Mục đích | Chờ N task hoàn thành | Chờ N thread gặp nhau tại barrier |
| Tái sử dụng | Không (count xuống 0 thì xong) | Có (reset() hoặc tự động reset khi tất cả party đến) |
| Constructor | CountDownLatch(int count) | CyclicBarrier(int parties) hoặc CyclicBarrier(int parties, Runnable barrierAction) |
| Thread/Task | Đếm task (countDown) | Đếm thread/party (await) |
| Phương thức chờ | await() (cho thread main/manager) | await() (cho các worker thread) |
| Phương thức giảm count | countDown() (gọi khi task xong) | Tự động khi thread gọi await() |
| Barrier Action | Không có | Có (Runnable chạy khi tất cả party đã đến, trước khi release) |
| Exception khi interrupt | Chỉ thread bị interrupt ném InterruptedException | Tất cả thread chờ khác ném BrokenBarrierException |
| Advanceable | Có thể tăng count thủ công? Không (chỉ countDown) | Không (không tăng parties được) |
Ví dụ CountDownLatch: Main thread chờ workers khởi tạo xong
import java.util.concurrent.*;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int workerCount = 3;
CountDownLatch latch = new CountDownLatch(workerCount);
ExecutorService executor = Executors.newFixedThreadPool(workerCount);
for (int i = 1; i <= workerCount; i++) {
final int id = i;
executor.submit(() -> {
try {
Thread.sleep(1000 + id * 500); // Giả lập khởi tạo
System.out.println("Worker " + id + " ready");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // Báo xong việc
}
});
}
System.out.println("Main: Waiting for workers...");
latch.await(); // Block cho đến khi count = 0
System.out.println("Main: All workers ready! Starting app...");
executor.shutdown();
}
}
Ví dụ CyclicBarrier: Threads đồng bộ tại checkpoint (ví dụ: game multiplayer, map-reduce)
import java.util.concurrent.*;
public class CyclicBarrierDemo {
public static void main(String[] args) {
int parties = 3;
// Barrier action: chạy khi đủ 3 thread await()
CyclicBarrier barrier = new CyclicBarrier(parties, () ->
System.out.println(">>> All parties arrived! Barrier action executed."));
ExecutorService executor = Executors.newFixedThreadPool(parties);
for (int i = 1; i <= parties; i++) {
final int id = i;
executor.submit(() -> {
try {
System.out.println("Thread " + id + " doing phase 1...");
Thread.sleep(500 + id * 300);
barrier.await(); // Chờ ở barrier 1
System.out.println("Thread " + id + " doing phase 2...");
Thread.sleep(300);
barrier.await(); // Chờ ở barrier 2 (tái sử dụng!)
System.out.println("Thread " + id + " finished.");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
Khi nào dùng cái nào?
- CountDownLatch: Khởi tạo tài nguyên (DB pool, cache, config), chờ nhiều service start xong, parallel computation chờ kết quả.
- CyclicBarrier: Phân chia công việc phase-based (map-reduce, game loop, pipeline stages), cần tái sử dụng barrier nhiều lần.
Lưu ý: Phaser (Java 7+)
Phaser linh hoạt hơn cả hai: hỗ trợ dynamic registration/deregistration parties, phase number, tiered synchronization. Dùng cho phức tạp hơn.