CyclicBarrier
CyclicBarrier is a synchronization aid that allows a fixed number of threads to wait for each other at a common barrier point, then all proceed simultaneously. Unlike CountDownLatch, which is one-shot, CyclicBarrier automatically resets after each barrier trip, making it suitable for algorithms that execute in repeated phases — each phase ends when all parties arrive at the barrier, the optional barrier action runs, and all threads proceed to the next phase. CyclicBarrier is symmetric: every thread both contributes to the barrier count (by calling await()) and waits for all others. This distinguishes it from CountDownLatch, where the thread counting down need not be the one waiting. The barrier action — an optional Runnable passed at construction — runs exactly once per barrier trip, in the context of the last-arriving thread, before any waiting threads are released. If any thread is interrupted or times out while waiting, all waiting threads receive BrokenBarrierException, and the barrier enters a broken state. This entry covers the full CyclicBarrier API, the broken barrier state and recovery, the barrier action and its threading semantics, iterative parallel algorithm patterns, and comparison with CountDownLatch and Phaser.
CyclicBarrier API — Construction, await, and Barrier Action
// ── Basic CyclicBarrier: 3 threads synchronized at a meeting point ─────
CyclicBarrier barrier = new CyclicBarrier(3, () ->
System.out.println("=== Barrier reached — all 3 threads proceed ==="));
for (int i = 0; i < 3; i++) {
final int id = i;
new Thread(() -> {
try {
System.out.println("Thread " + id + ": working");
Thread.sleep((long)(Math.random() * 300));
System.out.println("Thread " + id + ": arrived at barrier");
int index = barrier.await(); // blocks until all 3 arrive
// index = 0 for last to arrive, 2 for first
System.out.println("Thread " + id + ": released (arrival index=" + index + ")");
} catch (BrokenBarrierException | InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
// Thread 0: working
// Thread 1: working
// Thread 2: working
// Thread 1: arrived at barrier ← order varies
// Thread 0: arrived at barrier
// Thread 2: arrived at barrier
// === Barrier reached — all 3 threads proceed === ← barrier action runs
// Thread 2: released (arrival index=0) ← last to arrive
// Thread 0: released (arrival index=2)
// Thread 1: released (arrival index=1)
// ── Automatic reset: barrier is reused across phases ─────────────────
CyclicBarrier phaseBarrier = new CyclicBarrier(3);
for (int phase = 1; phase <= 3; phase++) {
final int p = phase;
Thread[] workers = new Thread[3];
for (int i = 0; i < 3; i++) {
final int id = i;
workers[i] = new Thread(() -> {
try {
System.out.printf("Phase %d, Thread %d: working%n", p, id);
Thread.sleep(50);
phaseBarrier.await(); // barrier automatically resets after each phase
System.out.printf("Phase %d, Thread %d: done%n", p, id);
} catch (BrokenBarrierException | InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
for (Thread w : workers) w.start();
for (Thread w : workers) w.join();
System.out.println("--- Phase " + p + " complete ---");
}
// ── getNumberWaiting and isBroken ────────────────────────────────────
CyclicBarrier inspected = new CyclicBarrier(5);
System.out.println("Parties: " + inspected.getParties()); // 5
System.out.println("Waiting: " + inspected.getNumberWaiting()); // 0
System.out.println("Broken: " + inspected.isBroken()); // falseBroken Barrier State and Error Recovery
// ── Broken barrier: timeout causes BrokenBarrierException for all ────
CyclicBarrier breakable = new CyclicBarrier(3);
AtomicInteger brokenCount = new AtomicInteger(0);
// Thread 1: times out waiting
new Thread(() -> {
try {
breakable.await(100, TimeUnit.MILLISECONDS); // times out
} catch (TimeoutException e) {
System.out.println("Thread 1: timed out — barrier broken");
} catch (BrokenBarrierException | InterruptedException e) {
System.out.println("Thread 1: broken barrier");
}
brokenCount.incrementAndGet();
}).start();
// Thread 2: receives BrokenBarrierException due to thread 1's timeout
new Thread(() -> {
try {
Thread.sleep(200); // arrives after thread 1 times out
breakable.await(); // BrokenBarrierException — barrier is already broken
} catch (BrokenBarrierException e) {
System.out.println("Thread 2: barrier was broken — aborting");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
brokenCount.incrementAndGet();
}).start();
Thread.sleep(500);
System.out.println("isBroken: " + breakable.isBroken()); // true
System.out.println("Affected: " + brokenCount.get()); // 2
// ── Recovery: reset() after handling the break ────────────────────────
System.out.println("Resetting barrier");
breakable.reset();
System.out.println("isBroken after reset: " + breakable.isBroken()); // false
// Barrier can now be used again for a new set of 3 threads
// ── Barrier action exception breaks the barrier ───────────────────────
CyclicBarrier fragileBarrier = new CyclicBarrier(2, () -> {
throw new RuntimeException("Barrier action failed!");
});
CountDownLatch observed = new CountDownLatch(2);
for (int i = 0; i < 2; i++) {
final int id = i;
new Thread(() -> {
try {
fragileBarrier.await();
} catch (BrokenBarrierException e) {
System.out.println("Thread " + id + ": got BrokenBarrierException");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (RuntimeException e) {
// Last-arriving thread gets the RuntimeException from the barrier action:
System.out.println("Thread " + id + ": got RuntimeException from action: " + e.getMessage());
} finally {
observed.countDown();
}
}).start();
}
observed.await();
// Thread 1: got RuntimeException from action: Barrier action failed!
// Thread 0: got BrokenBarrierException
// ── Resilient iterative algorithm with break detection ─────────────────
public static void resilientPhaseLoop(int parties, int phases, Runnable[] work)
throws InterruptedException {
CyclicBarrier barrier = new CyclicBarrier(parties, () ->
System.out.println("Phase complete"));
CountDownLatch allDone = new CountDownLatch(parties);
for (int i = 0; i < parties; i++) {
final int id = i;
new Thread(() -> {
try {
for (int phase = 0; phase < phases; phase++) {
work[id].run();
barrier.await(); // synchronize between phases
}
} catch (BrokenBarrierException e) {
System.out.println("Worker " + id + ": barrier broken, aborting");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
allDone.countDown();
}
}).start();
}
allDone.await();
}Iterative Parallel Algorithms and Comparison with CountDownLatch
// ── Parallel prefix sum (scan) using CyclicBarrier ───────────────────
// Classic iterative parallel algorithm: O(log n) phases, each with n/2 additions
public static int[] parallelPrefixSum(int[] input) throws InterruptedException {
int n = input.length;
int[] data = Arrays.copyOf(input, n);
int phases = (int)(Math.log(n) / Math.log(2));
// Barrier action validates intermediate result (optional but illustrative):
CyclicBarrier barrier = new CyclicBarrier(n / 2, () ->
System.out.println("Phase complete, data: " + Arrays.toString(data)));
CountDownLatch done = new CountDownLatch(n / 2);
for (int t = 0; t < n / 2; t++) {
final int tid = t;
new Thread(() -> {
try {
for (int phase = 0; phase < phases; phase++) {
int stride = 1 << phase; // 1, 2, 4, 8, ...
int idx = (tid + 1) * 2 * stride - 1;
if (idx < n) {
data[idx] += data[idx - stride];
}
barrier.await(); // all threads sync before next phase
}
} catch (BrokenBarrierException | InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
done.countDown();
}
}).start();
}
done.await();
return data;
}
int[] result = parallelPrefixSum(new int[]{1, 2, 3, 4, 5, 6, 7, 8});
System.out.println(Arrays.toString(result)); // [1, 3, 6, 10, 15, 21, 28, 36]
// ── Game engine update loop ────────────────────────────────────────────
public class GameLoop {
private static final int THREADS = 4;
private final CyclicBarrier physicsBarrier = new CyclicBarrier(THREADS, this::aggregatePhysics);
private final CyclicBarrier renderBarrier = new CyclicBarrier(THREADS, this::prepareRender);
private volatile boolean running = true;
public void start() {
for (int i = 0; i < THREADS; i++) {
final int id = i;
new Thread(() -> gameThread(id)).start();
}
}
private void gameThread(int id) {
while (running) {
try {
updatePhysics(id); // each thread updates its entities
physicsBarrier.await(); // wait for all physics to complete
updateAI(id); // AI reads physics results — safe to read
renderBarrier.await(); // wait for all AI to complete
submitDrawCalls(id); // submit draw calls from completed AI state
} catch (BrokenBarrierException | InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
private void aggregatePhysics() { /* merge broadphase results */ }
private void prepareRender() { /* swap buffers */ }
private void updatePhysics(int id) { /* simulate assigned entities */ }
private void updateAI(int id) { /* update AI for assigned entities */ }
private void submitDrawCalls(int id){ /* submit render commands */ }
}
// ── CyclicBarrier vs CountDownLatch summary ───────────────────────────
//
// CyclicBarrier CountDownLatch
// Reusable? Yes (auto-reset) No (one-shot)
// Symmetric? Yes (all call await) No (countDown ≠ await callers)
// Party count Fixed at construction Fixed at construction
// Barrier action Yes (runs between) No
// Broken state Yes (BrokenBarrier) No (interruption propagates)
// Best for Iterative phases Task completion, starting guns