CyclicBarrier, Phaser and Exchanger

Multithreading · lesson 32 of 38 · 4 min read

Synchronizers that reset: hold every worker at the end of a round, then release them together.

Open this lesson in the learning hub

Key points

  • CountDownLatch counts down once and is done. CyclicBarrier resets itself, so it works round after round.
  • await() blocks until all parties arrive. The last arrival runs the optional barrier action, then everyone continues.
  • If a party dies the barrier breaks: every other waiter gets a BrokenBarrierException instead of hanging forever.
  • Phaser is a barrier whose party count can change: register() and arriveAndDeregister() at any phase.
  • Exchanger pairs two threads and swaps a value between them, which suits fill-one-buffer-drain-the-other loops.

Example

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        int workers = 3, rounds = 3;
        AtomicInteger merges = new AtomicInteger();

        // The barrier action runs once per round, on the last thread to arrive.
        CyclicBarrier barrier = new CyclicBarrier(workers,
                () -> System.out.println("round " + merges.incrementAndGet() + " merged, all 3 released"));

        ExecutorService pool = Executors.newFixedThreadPool(workers);
        for (int w = 1; w <= workers; w++) {
            int id = w;
            pool.execute(() -> {
                try {
                    for (int r = 1; r <= rounds; r++) {
                        Thread.sleep(10L * id);        // deliberately uneven work
                        barrier.await();               // nobody starts the next round early
                    }
                } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                catch (BrokenBarrierException e) { System.out.println("barrier broke"); }
            });
        }
        pool.shutdown();
        pool.awaitTermination(5, TimeUnit.SECONDS);
        System.out.println("barrier reused : " + merges.get() + " times (a latch fires only once)");

        Phaser phaser = new Phaser(1);                 // main registers itself
        for (int w = 1; w <= 2; w++) {
            phaser.register();                         // parties can join at any phase
            int id = w;
            new Thread(() -> {
                System.out.println("phaser worker " + id + " arrived in phase " + phaser.getPhase());
                phaser.arriveAndDeregister();          // and leave again, unlike a barrier
            }).start();
        }
        phaser.arriveAndAwaitAdvance();
        System.out.println("phase now      : " + phaser.getPhase() + ", parties left " + phaser.getRegisteredParties());
    }
}

Latch for one gate, barrier for repeated rounds, phaser when the crowd changes size.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Multithreading course, and every lesson in it is listed on the Multithreading contents page.