What is a Phaser and how does it improve on CyclicBarrier and CountDownLatch?
answer
- Reusable + multi-phase + dynamic parties
- register / bulkRegister / arriveAndDeregister
- arriveAndAwaitAdvance is the workhorse
- onAdvance hook controls phase boundary + termination
- Tiering for high party counts
basics
~20 sA Phaser is a reusable barrier where threads wait for each other at the end of each phase. Unlike CountDownLatch and CyclicBarrier, the number of parties can grow or shrink at runtime, and it supports many phases.
solid answer
~50 sPhaser is a flexible synchronization barrier that combines and extends CountDownLatch (one-shot countdown) and CyclicBarrier (reusable N-party rendezvous). Like CyclicBarrier it is reusable across multiple phases, but its party count is dynamic: threads call register()/bulkRegister() to join and arriveAndDeregister() to leave, so the barrier width can change between phases. The core call is arriveAndAwaitAdvance(): a party signals arrival and blocks until all registered parties have arrived, at which point the phaser advances to the next phase (its phase number increments) and everyone proceeds. arrive() and awaitAdvance(phase) split those steps. A non-blocking arriveAndDeregister() lets a party drop out without waiting. You can override onAdvance(phase, parties) to run logic at each phase boundary and to decide when the phaser terminates (returning true). Phasers also support tiering for scalability. This makes Phaser the right tool for iterative/multi-stage algorithms where the set of participants varies per round.
code
java · 15 linesPhaser phaser = new Phaser(1); // "1" registers the main/controller thread
for (int i = 0; i < workerCount; i++) {
phaser.register(); // each worker joins
final int id = i;
new Thread(() -> {
for (int phase = 0; phase < 3; phase++) {
doWork(id, phase);
phaser.arriveAndAwaitAdvance(); // wait for all at end of each phase
}
phaser.arriveAndDeregister(); // leave when done
}).start();
}
phaser.arriveAndDeregister(); // controller drops out; workers run the phasesgo deeper
Knows a Phaser is a reusable barrier that threads wait at, and that it is more flexible than CountDownLatch.
Can use arriveAndAwaitAdvance in a loop for multi-phase work and register/deregister workers; explains it is reusable unlike a latch.
Articulates the dynamic-party advantage over CyclicBarrier, splits arrive/await, uses onAdvance for phase logic and termination, and chooses Phaser vs barrier vs latch appropriately.
Reasons about tiering for scalability at high party counts, designs termination/deregistration to avoid deadlocking survivors, and weighs Phaser against modern alternatives (structured concurrency, CompletableFuture) for the team.
## The family it belongs to Java has three related "wait for each other" tools: - **`CountDownLatch`** — a *one-shot* gate. You initialize it with a count; threads `countDown()` it; waiters `await()` until it hits zero. Once zero, it is done forever (not reusable). - **`CyclicBarrier`** — a *reusable* rendezvous for a *fixed* number N of parties. Each calls `await()`; when the Nth arrives, all are released and the barrier resets for the next round. - **`Phaser`** — the most flexible: **reusable** like a barrier, **multi-phase**, and crucially with a **dynamic** number of parties that can change at runtime. ## Key vocabulary - **Party** — a participant registered with the phaser. The phaser tracks how many are registered. - **Phase** — one round of the barrier. Each time all registered parties arrive, the phaser **advances**: its internal **phase number** (an int starting at 0) increments and all waiters are released together. - **Arrive** — a party signalling it has reached the barrier for this phase. ## Registering and deregistering (the dynamic part) This is what sets Phaser apart. The party count is not fixed at construction: - `new Phaser(n)` starts with `n` parties; `new Phaser()` starts with 0. - `register()` adds one party; `bulkRegister(k)` adds `k` — you can do this *while the phaser is running* to add participants for upcoming phases. - `arriveAndDeregister()` signals arrival *and* removes this party from future phases — used when a worker finishes its work and should no longer hold up the barrier. With `CyclicBarrier` the count is fixed forever; Phaser lets the width grow and shrink between rounds, which is exactly what fork/join-style or iterative algorithms need. ## The arrival methods - `arriveAndAwaitAdvance()` — the everyday call: "I've arrived; block me until everyone else for this phase has too, then advance." Returns the new phase number. - `arrive()` — signal arrival but **do not** wait; returns the current phase. Useful when a thread should announce it's done but keep working on something else. - `awaitAdvance(int phase)` — block until the phaser leaves the given phase. Pairs with `arrive()` to split announce-and-wait into two steps. - `arriveAndDeregister()` — arrive and leave the party set (non-blocking). ## The onAdvance hook You can subclass `Phaser` and override `protected boolean onAdvance(int phase, int registeredParties)`. It runs **once per phase boundary**, on the thread that triggers the advance, *before* releasing the others — a place to run between-phase logic (like a `CyclicBarrier` barrier action). Its return value controls **termination**: return `true` to terminate the phaser (after which `isTerminated()` is true and arrivals no longer block). The default implementation terminates when the registered party count drops to zero. ## Termination A phaser is **terminated** when `onAdvance` returns true or `forceTermination()` is called. After termination, await methods return immediately (with a negative phase number) instead of blocking — important so deregistering/finishing threads don't deadlock survivors. ## Tiering (scalability) For very large party counts, contention on a single phaser becomes a bottleneck. Phasers can be arranged in a **tree** (each child phaser has a parent), spreading synchronization cost. You rarely need this, but it's why Phaser scales better than CyclicBarrier at high party counts. ## When to choose it Reach for Phaser when: the number of participants varies between rounds (workers join/leave), you have multiple phases (iterative simulations, multi-stage pipelines), or you want a non-blocking arrive plus a per-phase hook. If the party count is fixed and you just need a repeated rendezvous, `CyclicBarrier` is simpler; if it's a single one-shot gate, `CountDownLatch` is simpler.
- What is the difference between arriveAndAwaitAdvance() and arriveAndDeregister()?arriveAndAwaitAdvance() signals arrival and blocks until all parties arrive, then continues into the next phase - the party stays registered. arriveAndDeregister() signals arrival, removes the party from the count, and does NOT block, so the thread can leave without holding up the barrier.
- How do you make a Phaser terminate after a fixed number of phases?Subclass Phaser and override onAdvance(int phase, int parties) to return true once phase reaches the desired count (e.g. return phase >= maxPhases - 1 || parties == 0). Returning true terminates the phaser so subsequent await calls no longer block.
A relay race where runners can join or drop out between laps: each lap (phase) everyone at the line waits for the rest, then they all start the next lap together - and the roster can change lap to lap.
saying these in an interview costs you the question
- Saying Phaser has a fixed party count like CyclicBarrier - its parties are dynamic via register/deregister
- Confusing it with the one-shot CountDownLatch - Phaser is reusable across many phases
- Forgetting that arriveAndDeregister does NOT wait, unlike arriveAndAwaitAdvance
- Believing onAdvance runs on every arrival - it runs once per phase boundary, on the advancing thread