Explain how a Phaser's phase number works and how termination changes the behavior of its await methods.
answer
- Phase number: int from 0, ++ each completed round
- Methods return the phase you advanced into
- Wraps after Integer.MAX_VALUE (cyclic, not infinite)
- Terminated => negative phase, await returns immediately
- onAdvance true / forceTermination => terminate; default at parties==0
basics
~10 sEach completed round increments the phase number (an int starting at 0). When the phaser terminates, await methods stop blocking and immediately return a negative number, so finishing threads can't deadlock the rest.
solid answer
~50 sA Phaser tracks a phase number: an int starting at 0 that increments by one each time all registered parties arrive and the phaser advances. Methods like arriveAndAwaitAdvance() and awaitAdvance(phase) return the new phase, so code can branch on the round. The phase number wraps around after Integer.MAX_VALUE back to zero - you should compare phases for equality/advance, not assume monotonic growth forever. A phaser becomes terminated when onAdvance(phase, parties) returns true (the default returns true once registered parties hit zero) or when forceTermination() is called. Termination is the critical safety mechanism: after it, all await/arrive methods return immediately with a negative phase number instead of blocking, and isTerminated() returns true. This guarantees that parties which deregister or finish can't strand survivors waiting on a barrier that will never complete. So designing onAdvance termination correctly is how you avoid both premature release and permanent blocking in dynamic-membership scenarios.
code
java · 14 linesPhaser phaser = new Phaser(3) {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
// terminate after 5 phases, or when everyone has left
return phase >= 4 || registeredParties == 0;
}
};
// In a worker:
while (!phaser.isTerminated()) {
doWork();
int next = phaser.arriveAndAwaitAdvance(); // returns next phase, or negative if terminated
if (next < 0) break; // terminated: stop cleanly
}go deeper
Knows the phase number counts rounds and that a phaser can be terminated.
Uses the returned phase to branch per round and checks isTerminated() to exit loops.
Explains onAdvance-driven termination, the default parties==0 rule, and that terminated await calls return a negative phase immediately.
Designs onAdvance termination to avoid premature release and deadlock, accounts for phase wraparound and tiering, and treats termination as the safety contract that makes dynamic deregistration sound.
## What the phase number is A `Phaser` maintains an internal **phase number** — an `int` that starts at **0**. Each time *all currently registered parties* have arrived at the barrier, the phaser **advances**: the phase number increments by one and all waiting parties are released into the next round simultaneously. So the phase number is essentially "how many rounds have completed." Many methods return it: - `arriveAndAwaitAdvance()` returns the **arriving phase's successor** — i.e. the phase you've advanced into. - `awaitAdvance(int phase)` blocks until the phaser leaves `phase`, then returns the next phase number. - `getPhase()` reads the current phase without participating. Returning the phase lets code make per-round decisions (e.g. "on phase 5, switch strategy"). ## Wraparound Because the phase number is a 32-bit `int`, after `Integer.MAX_VALUE` it **wraps around** back to 0. This is rarely hit in practice, but the design implication is: treat the phase number as a *cyclic* identifier (compare for the specific phase you're waiting on), not as an ever-increasing counter you can index forever. ## Negative phase numbers signal termination When a phaser is **terminated**, its phase number becomes **negative** (the high bit set). This is the runtime signal: any method that returns a phase will return a negative value once the phaser has terminated. Code can check `phaser.isTerminated()` or test for a negative return. ## How termination happens Two routes: 1. **`onAdvance(int phase, int registeredParties)` returns `true`.** This protected hook runs at every phase boundary, on the thread triggering the advance, before releasing others. If it returns `true`, the phaser terminates. The **default** implementation returns `true` exactly when `registeredParties == 0` — i.e. the phaser auto-terminates once the last party deregisters. Override it to terminate after a fixed number of phases, on a convergence condition, etc. 2. **`forceTermination()`** — an explicit, immediate termination, typically used to unblock everyone during shutdown or after an error. ## Why termination changes await behavior — the deadlock-avoidance contract This is the subtle, principal-level point. In a **dynamic-membership** phaser, parties come and go via `register()`/`arriveAndDeregister()`. Consider the danger: a thread is blocked in `arriveAndAwaitAdvance()` waiting for the rest, but every *other* party has deregistered and left. With naive semantics the blocked thread would wait forever for arrivals that can never come. Phaser prevents this: **once terminated, all `await*`/`arrive*` methods return immediately** (with a negative phase) instead of blocking. Because the default `onAdvance` terminates when parties reach zero, the moment the last peer deregisters the phaser terminates and the stranded waiter is released. Termination is therefore not just a 'stop' flag — it is the mechanism that makes dynamic deregistration safe. ## Design guidance - **Own your `onAdvance`** when you need deterministic termination (e.g. `return phase >= maxPhases - 1 || registeredParties == 0;`). Returning `true` too early releases parties prematurely; never returning `true` (and never reaching zero parties) risks an unending phaser. - **Check the return / isTerminated()** after each await so loops exit cleanly rather than re-entering a terminated barrier. - **Use `forceTermination()`** in error/shutdown paths to guarantee no thread is left blocked. - Remember phase **wraparound** if you build very long-running phasers keyed on absolute phase counts. ## Relation to scalability None of this changes under **tiering** (parent/child phasers for high party counts): each tier still advances and reports phase numbers consistently; termination propagates so the whole tree releases. The contract — advance increments, termination releases — is uniform.
- What value do Phaser's await methods return once the phaser is terminated, and why does it matter?They return a negative phase number and return immediately instead of blocking. This matters because it releases any thread that was waiting at the barrier, preventing a deadlock when other parties have deregistered or the phaser was force-terminated.
- When does the default onAdvance terminate the phaser?The default onAdvance returns true (terminating) exactly when the registered party count reaches zero - i.e. after the last party deregisters - so a phaser cleans itself up automatically once everyone has left.
A turnstile counter at a stadium that resets each event night: it ticks up per round, but when the venue closes (termination) the gates auto-open so no one is trapped waiting inside.
saying these in an interview costs you the question
- Assuming the phase number grows without bound - it wraps at Integer.MAX_VALUE
- Thinking await methods keep blocking after termination - they return immediately with a negative phase
- Believing onAdvance only runs once - it runs at every phase boundary
- Ignoring termination design, risking either premature release or a permanently blocked phaser