Back to Synchronization Utilities: Latch, Barrier, Semaphore
Q4Question 4 of 6

What is a Phaser?

Key Concepts
  • Phaser (Java 7) combines and extends CountDownLatch and CyclicBarrier with dynamic party registration and automatic multi-phase advancement.
  • Parties can register() to join and arriveAndDeregister() to leave at any phase, unlike Latch and Barrier which require fixed counts at construction.
  • arriveAndAwaitAdvance() is the primary operation: arrive at the current phase, wait for all parties, then advance to the next phase.
  • onAdvance(phase, parties) is a callback that runs between phases, returning true to terminate the Phaser.
  • Phaser supports hierarchical tiering: child Phasers can register with a parent to reduce contention in large-scale synchronization.
  • Use Phaser for multi-phase algorithms with variable participants, like ETL pipelines where the number of workers changes between stages.
  • CyclicBarrier resets to the same party count after each barrier; Phaser allows different party counts at each phase.

Definition

Phaser combines CountDownLatch and CyclicBarrier with dynamic party registration (join/leave at any phase) and automatic multi-phase advancement, making it the most flexible synchronization barrier in java.util.concurrent.
  • Dynamic registration – parties can join with register() and leave with arriveAndDeregister() at any time during any phase. CountDownLatch and CyclicBarrier require fixed party counts at construction.
  • Multi-phase – automatically advances through numbered phases (0, 1, 2, ...). Each call to arriveAndAwaitAdvance() blocks until all registered parties arrive, then all advance to the next phase together.
  • onAdvance callback – onAdvance(int phase, int registeredParties) runs after all parties arrive and before the next phase begins. Return true to terminate the Phaser.
  • Hierarchical tiering – child Phasers can register with a parent Phaser to reduce CAS contention in large-scale synchronization (hundreds of threads).
  • Termination – the Phaser terminates when onAdvance() returns true, when all parties deregister, or when forceTermination() is called.

CountDownLatch is a one-time gate at National Bank of India: wait for exactly 5 tellers to arrive, then open the gate. You cannot change the count to 6. CyclicBarrier is a checkpoint gate: exactly 5 auditors synchronize at each checkpoint, but you cannot add a 6th mid-audit. Phaser is an adaptive meeting: auditors can join and leave between meetings. Meeting 1: 5 auditors. Meeting 2: 3 left, 2 new ones joined, so 4 auditors. Meeting 3: 1 more leaves, 3 synchronize. No fixed count required.

Understanding the Concepts

What is a Phaser?

Phaser is the most flexible synchronization barrier in java.util.concurrent. It supports dynamic party registration, multiple phases, and a callback between phases. It can replace both CountDownLatch (one-time wait) and CyclicBarrier (fixed-count reusable barrier) while adding capabilities neither provides.

JAVA
package com.bank.concurrency;

import java.util.concurrent.Phaser;

public class PhaserDemo {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(3);  // 3 initial parties

        for (int i = 0; i < 3; i++) {
            final int workerId = i;
            new Thread(() -> {
                System.out.println("Worker-" + workerId + ": phase 0 work");
                phaser.arriveAndAwaitAdvance();  // sync at phase 0→1

                System.out.println("Worker-" + workerId + ": phase 1 work");
                phaser.arriveAndAwaitAdvance();  // sync at phase 1→2

                System.out.println("Worker-" + workerId + ": done");
                phaser.arriveAndDeregister();     // leave
            }, "Worker-" + i).start();
        }
    }
}

How does dynamic registration work?

JAVA
package com.bank.concurrency;

import java.util.concurrent.Phaser;

public class DynamicRegistrationDemo {
    public static void main(String[] args) throws InterruptedException {
        Phaser phaser = new Phaser(1);  // main thread is party 1

        // Dynamically add workers
        for (int i = 0; i < 3; i++) {
            phaser.register();  // add a party
            final int id = i;
            new Thread(() -> {
                System.out.println("Worker-" + id + " processing");
                phaser.arriveAndDeregister();  // done, leave
            }, "Worker-" + id).start();
        }

        phaser.arriveAndAwaitAdvance();  // main waits for all workers
        System.out.println("All workers complete");
    }
}

How does onAdvance work?

JAVA
package com.bank.concurrency;

import java.util.concurrent.Phaser;

public class OnAdvanceDemo {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(3) {
            @Override
            protected boolean onAdvance(int phase, int registeredParties) {
                System.out.println("Phase " + phase + " complete. Parties: "
                        + registeredParties);
                return phase >= 2 || registeredParties == 0;
                // Terminate after phase 2 or when all parties leave
            }
        };
        // ... use phaser in worker threads
    }
}

How is Phaser different from CountDownLatch and CyclicBarrier?

| Feature | CountDownLatch | CyclicBarrier | Phaser |
|---|---|---|---|
| Fixed parties | Yes (at construction) | Yes (at construction) | No (dynamic) |
| Reusable | No (one-time) | Yes (auto-reset) | Yes (auto-advance) |
| Phases | 1 (count to 0) | Multiple (trips) | Multiple (numbered) |
| Join/leave mid-run | No | No | Yes (register/deregister) |
| Callback | No | Yes (barrier action) | Yes (onAdvance) |
| Termination | count reaches 0 | reset() | onAdvance returns true |

What is a real-world banking use case?

An ETL pipeline at National Bank of India processes data in three phases: extract, transform, and load. The number of workers varies between phases because some workers finish early or new workers are added for the heavier phases.

JAVA
package com.bank.etl;

import java.util.concurrent.Phaser;

public class ETLPipelineDemo {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(1);  // coordinator

        // Phase 0: Extract (10 workers)
        for (int i = 0; i < 10; i++) startWorker(phaser, "Extract-" + i);
        phaser.arriveAndAwaitAdvance();  // wait for phase 0

        // Phase 1: Transform (5 workers, others deregistered)
        for (int i = 0; i < 5; i++) startWorker(phaser, "Transform-" + i);
        phaser.arriveAndAwaitAdvance();  // wait for phase 1

        // Phase 2: Load (3 workers)
        for (int i = 0; i < 3; i++) startWorker(phaser, "Load-" + i);
        phaser.arriveAndAwaitAdvance();  // wait for phase 2

        phaser.arriveAndDeregister();
        System.out.println("ETL pipeline complete");
    }

    static void startWorker(Phaser phaser, String name) {
        phaser.register();
        new Thread(() -> {
            System.out.println(name + " processing");
            phaser.arriveAndDeregister();
        }, name).start();
    }
}

What are common interview follow-up questions?

  • Can a Phaser deadlock? Yes, if a registered party never arrives. Use awaitAdvanceInterruptibly() with a timeout to prevent permanent blocking.
  • What is hierarchical tiering? Large Phasers (hundreds of parties) suffer CAS contention on the internal state. Tiering groups parties under child Phasers that register with a parent, distributing the synchronization overhead.

Visual Overview

DIAGRAM
  Phaser: Dynamic Parties Across Phases
  ═════════════════════════════════════

  Phase 0 (Extract):  W1  W2  W3  W4  W5  (5 parties)
  ──── arriveAndAwaitAdvance() ────
  Phase 1 (Transform): W1  W2  W3  (W4, W5 deregistered)
  ──── arriveAndAwaitAdvance() ────
  Phase 2 (Load):      W1  W6  (W2, W3 deregistered, W6 registered)
  ──── arriveAndAwaitAdvance() ────
  Terminated (onAdvance returns true)

Key Takeaway

Phaser is the most flexible synchronization barrier in java.util.concurrent, combining CountDownLatch's one-time wait with CyclicBarrier's reusable checkpoints and adding dynamic party registration that neither provides. Parties can join with register() and leave with arriveAndDeregister() at any phase, making it ideal for multi-phase algorithms where the number of participants changes between stages.

The onAdvance callback runs between phases and can terminate the Phaser when processing is complete. For large-scale synchronization (hundreds of threads), hierarchical tiering distributes CAS contention across child Phasers registered with a parent.

At National Bank of India, an ETL pipeline used Phaser to coordinate variable worker counts across three phases: 10 extract workers, 5 transform workers, and 3 load workers. CyclicBarrier could not handle the shrinking party count, but Phaser handled it naturally with arriveAndDeregister().

Remember one sentence: Phaser is the dynamic-party, multi-phase synchronization barrier that lets threads join and leave at any phase, replacing both CountDownLatch and CyclicBarrier.

---

Share
Use ← → arrow keys to navigate