| Feature | CountDownLatch | CyclicBarrier | Phaser |
| Reusable | No | Yes (auto-reset) | Yes (multiple phases) |
| Dynamic parties | No | No | Yes — register/deregister at any time |
| Phase tracking | No | Implicit | Yes — explicit phase number |
| Termination | Manual | Manual | Built-in via onAdvance() |
| Tiered hierarchy | No | No | Yes — parent Phaser for large groups |
| Complexity | Low | Medium | High |
import java.util.concurrent.Phaser;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PhaserExample {
public static void main(String[] args) {
int parties = 3;
// Register the main thread + N workers
Phaser phaser = new Phaser(1 + parties); // 1 for main thread
ExecutorService pool = Executors.newFixedThreadPool(parties);
for (int i = 0; i < parties; i++) {
final int id = i;
pool.submit(() -> {
// Phase 1: data loading
System.out.println("Worker " + id + " loading data (phase " + phaser.getPhase() + ")");
phaser.arriveAndAwaitAdvance(); // wait for all workers
// Phase 2: processing
System.out.println("Worker " + id + " processing (phase " + phaser.getPhase() + ")");
phaser.arriveAndAwaitAdvance();
// Phase 3: writing output
System.out.println("Worker " + id + " writing output (phase " + phaser.getPhase() + ")");
phaser.arriveAndDeregister(); // deregister when done
});
}
// Main thread participates in phase 1 and 2, then deregisters
phaser.arriveAndAwaitAdvance(); // wait for phase 1
System.out.println("Main: all data loaded");
phaser.arriveAndAwaitAdvance(); // wait for phase 2
System.out.println("Main: all processing done");
phaser.arriveAndDeregister(); // main done
pool.shutdown();
}
}
arriveAndAwaitAdvance() signals arrival and waits for all other parties. arriveAndDeregister() signals arrival and permanently removes the caller from future phases — the party count drops by one.
| Method | Description |
register() | Dynamically add one party; returns new phase number |
bulkRegister(n) | Add n parties at once |
arrive() | Signal arrival without waiting (non-blocking) |
arriveAndAwaitAdvance() | Signal arrival and block until phase advances |
arriveAndDeregister() | Signal arrival and remove self from future phases |
awaitAdvance(phase) | Wait for the given phase to complete (non-participating) |
awaitAdvanceInterruptibly(phase) | Same but responds to interruption |
getPhase() | Current phase number (starts at 0; negative when terminated) |
getRegisteredParties() | Number of registered parties |
getArrivedParties() | Number that have arrived for the current phase |
forceTermination() | Terminate the phaser immediately |
Parties can register at any time (even from within a running phase). This enables fork-join style patterns where tasks spawn sub-tasks that join the same phaser.
import java.util.concurrent.Phaser;
Phaser phaser = new Phaser(1); // start with just main thread
// Dynamically register and start workers
for (int i = 0; i < 5; i++) {
phaser.register(); // register before starting the thread
final int id = i;
new Thread(() -> {
System.out.println("Worker " + id + " in phase " + phaser.getPhase());
phaser.arriveAndDeregister(); // arrive and deregister when done
}).start();
}
// Main thread waits for all workers at phase 0
phaser.arriveAndAwaitAdvance();
System.out.println("Phase 0 complete; parties left: " + phaser.getRegisteredParties());
Override onAdvance(int phase, int registeredParties) to run code after each barrier trip and to decide when the Phaser should terminate. Returning true terminates it.
import java.util.concurrent.Phaser;
int maxPhases = 3;
Phaser phaser = new Phaser(3) {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
System.out.printf("Phase %d complete — %d parties remaining%n",
phase, registeredParties);
// Terminate after maxPhases phases or when all parties deregistered
return phase >= maxPhases - 1 || registeredParties == 0;
}
};
// Workers run until phaser terminates
Runnable task = () -> {
while (!phaser.isTerminated()) {
doPhaseWork(phaser.getPhase());
phaser.arriveAndAwaitAdvance();
}
System.out.println("Worker done — phaser terminated");
};
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
When a Phaser terminates, getPhase() returns a negative number. arriveAndAwaitAdvance() returns immediately on a terminated Phaser without throwing, making it safe to check isTerminated() in a loop.
A single Phaser with many parties has internal contention on its state word. For very large party counts, build a tree of Phasers — child Phasers propagate to a parent automatically.
import java.util.concurrent.Phaser;
// Root phaser — no registered parties itself; children auto-register
Phaser root = new Phaser();
// Each child phaser registers one party on the root
// and manages its own group of 100 workers
Phaser child1 = new Phaser(root, 100);
Phaser child2 = new Phaser(root, 100);
// Workers use child phasers
Runnable workerGroup1 = () -> {
child1.arriveAndAwaitAdvance(); // synchronise within group 1
};
// Root advances only after both children complete their phases
// This scales to millions of parties with O(log n) contention
import java.util.concurrent.Phaser;
Phaser phaser = new Phaser(3);
// A monitoring thread observes phase transitions without participating
Thread monitor = new Thread(() -> {
int phase = 0;
while (!phaser.isTerminated()) {
// Block until phase 'phase' completes
phase = phaser.awaitAdvance(phase) + 1;
System.out.println("Monitor: phase advanced to " + phaser.getPhase());
}
});
monitor.setDaemon(true);
monitor.start();