Phaser in Java: Flexible Multi-Phase Task Coordination

Phaser is the most flexible synchronization barrier in java.util.concurrent. Unlike CountDownLatch (one-shot) and CyclicBarrier (fixed parties, one phase), Phaser supports dynamic registration and deregistration of parties, multiple phases, optional termination, tiered hierarchies for large party counts, and a customisable onAdvance() hook.

Share

Phaser vs CountDownLatch vs CyclicBarrier

FeatureCountDownLatchCyclicBarrierPhaser
ReusableNoYes (auto-reset)Yes (multiple phases)
Dynamic partiesNoNoYes: register/deregister at any time
Phase trackingNoImplicitYes: explicit phase number
TerminationManualManualBuilt-in via onAdvance()
Tiered hierarchyNoNoYes: parent Phaser for large groups
ComplexityLowMediumHigh

Basic Phaser: Fixed Parties, Multiple Phases

Java
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.

Key Phaser Methods Reference

MethodDescription
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

Dynamic Registration: Parties Join Mid-Flight

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.

Java
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());

Custom onAdvance(): Controlling Termination

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.

Java
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.

Tiered Phaser: Handling Thousands of Parties

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.

Java
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

Non-Blocking Monitoring with awaitAdvance()

Java
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();