Feature | Phaser | CyclicBarrier | CountDownLatch |
---|---|---|---|
Purpose | Synchronize multiple threads across multiple phases. | Synchronize a fixed number of threads at a single point. | Wait for a set number of events or threads to complete. |
Dynamic Threading | Allows dynamic registration/deregistration of threads. | Number of threads is fixed at initialization. | Count is fixed at initialization. |
Phases | Supports multiple phases of synchronization. | Single synchronization point (can be reused). | Single-use latch (not reusable). |
Reset/Reuse | Yes, resets automatically for new phases. | Yes, can be reused after the barrier is tripped. | No, cannot be reused once the count reaches zero. |
API Complexity | Flexible and feature-rich. | Simple and straightforward. | Simple and straightforward. |
Thread Termination | Can terminate after reaching a specific condition. | Does not support termination. | No termination support, only counts down to zero. |
Best Use Case | Multi-phase tasks with dynamic participation. | Fixed-size thread pools or teams synchronizing at one point. | Tasks where one thread waits for others to complete. |
Examples
1. Phaser
Use case: Synchronizing threads in multiple phases with dynamic registration and deregistration.
import java.util.concurrent.Phaser;
public class PhaserExample {
public static void main(String[] args) {
Phaser phaser = new Phaser(1); // Main thread registers itself
for (int i = 1; i <= 3; i++) {
phaser.register();
final int threadId = i;
new Thread(() -> {
System.out.println("Thread " + threadId + " - Phase 1 started");
phaser.arriveAndAwaitAdvance();
System.out.println("Thread " + threadId + " - Phase 1 completed");
System.out.println("Thread " + threadId + " - Phase 2 started");
phaser.arriveAndAwaitAdvance();
System.out.println("Thread " + threadId + " - Phase 2 completed");
phaser.arriveAndDeregister();
}).start();
}
phaser.arriveAndAwaitAdvance(); // Main thread syncs Phase 1
System.out.println("Main thread - Phase 1 completed");
phaser.arriveAndAwaitAdvance(); // Main thread syncs Phase 2
System.out.println("Main thread - Phase 2 completed");
phaser.arriveAndDeregister(); // Deregister main thread
}
}
Output:
Thread 1 - Phase 1 started
Thread 2 - Phase 1 started
Thread 3 - Phase 1 started
Main thread - Phase 1 completed
Thread 1 - Phase 1 completed
Thread 2 - Phase 1 completed
Thread 3 - Phase 1 completed
Thread 1 - Phase 2 started
Thread 2 - Phase 2 started
Thread 3 - Phase 2 started
Main thread - Phase 2 completed
Thread 1 - Phase 2 completed
Thread 2 - Phase 2 completed
Thread 3 - Phase 2 completed
2. CyclicBarrier
Use case: Fixed number of threads synchronizing at a single barrier point.
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("All threads have reached the barrier, proceeding to the next step.");
});
for (int i = 1; i <= 3; i++) {
final int threadId = i;
new Thread(() -> {
System.out.println("Thread " + threadId + " is performing work.");
try {
Thread.sleep(1000); // Simulate work
System.out.println("Thread " + threadId + " is waiting at the barrier.");
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Thread " + threadId + " has crossed the barrier.");
}).start();
}
}
}
Output:
Thread 1 is performing work.
Thread 2 is performing work.
Thread 3 is performing work.
Thread 1 is waiting at the barrier.
Thread 2 is waiting at the barrier.
Thread 3 is waiting at the barrier.
All threads have reached the barrier, proceeding to the next step.
Thread 1 has crossed the barrier.
Thread 2 has crossed the barrier.
Thread 3 has crossed the barrier.
3. CountDownLatch
Use case: One thread waits for multiple threads or events to complete.
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 1; i <= 3; i++) {
final int threadId = i;
new Thread(() -> {
System.out.println("Thread " + threadId + " is performing work.");
try {
Thread.sleep(1000); // Simulate work
System.out.println("Thread " + threadId + " has completed work.");
latch.countDown(); // Decrement the latch count
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
try {
System.out.println("Main thread is waiting for other threads to finish.");
latch.await(); // Wait for the latch count to reach zero
System.out.println("All threads have finished, main thread continues.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Output:
Thread 1 is performing work.
Thread 2 is performing work.
Thread 3 is performing work.
Thread 1 has completed work.
Thread 2 has completed work.
Thread 3 has completed work.
Main thread is waiting for other threads to finish.
All threads have finished, main thread continues.
Summary
- Use Phaser for multi-phase tasks with dynamic participation.
- Use CyclicBarrier for fixed-thread synchronization at a single barrier point, with optional reuse.
- Use CountDownLatch for tasks where one thread waits for others to complete and the count is fixed.