Mastering Java’s CyclicBarrier: How Threads Synchronize and What Can Go Wrong
This article explains Java’s CyclicBarrier synchronization aid, detailing its purpose, constructors, the await() workflow, internal implementation with ReentrantLock and Condition, generation handling, error scenarios, timeout support, and provides a complete multithreaded example illustrating its practical use.
What is CyclicBarrier?
CyclicBarrier is a synchronization aid that allows a fixed set of threads to wait for each other at a common barrier point. When the last thread arrives, the barrier opens and all waiting threads proceed. It can be reused after release, hence the name “cyclic”.
Constructors
public CyclicBarrier(int parties)– creates a barrier for the given number of parties without a predefined barrier action. public CyclicBarrier(int parties, Runnable barrierAction) – creates a barrier that, when tripped, executes the supplied Runnable (run by the last arriving thread). parties specifies how many threads must call await() before the barrier is released. barrierAction is optional and can be used for additional processing.
Core Method: await()
The await() method blocks a thread until all parties have arrived. Internally it delegates to a private dowait(boolean timed, long nanos) method, which handles locking, generation tracking, barrier breaking, and optional timeout.
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L); // non‑timed wait
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}The dowait algorithm works as follows:
Acquire the internal ReentrantLock and obtain the current Generation object.
If the generation is already broken, throw BrokenBarrierException.
If the thread is interrupted, break the barrier and re‑throw InterruptedException.
Decrement the waiting count; if it reaches zero, the barrier is tripped:
Execute the optional barrierCommand if present.
Call nextGeneration() to wake all waiting threads, reset the count, and create a new generation.
Return index 0 to the last thread.
If the count is not zero, the thread waits on the associated Condition (either indefinitely or with a timeout).
When awakened, the thread checks for broken generation, interruption, or timeout and reacts accordingly, possibly throwing BrokenBarrierException or TimeoutException.
Supporting Classes and Methods
The barrier maintains a nested static class Generation that tracks whether the current generation is broken:
private static class Generation {
boolean broken = false;
}When a barrier is broken (e.g., due to interruption or an explicit reset()), breakBarrier() marks the generation as broken, resets the count, and signals all waiting threads:
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}After a successful trip, nextGeneration() wakes all threads, resets the count, and creates a fresh Generation object:
private void nextGeneration() {
trip.signalAll();
count = parties;
generation = new Generation();
}Timeout Variant
The overloaded method await(long timeout, TimeUnit unit) simply forwards to dowait(true, nanos), enabling timed waiting and throwing TimeoutException when the specified period expires.
Practical Example
The following example demonstrates five threads waiting at a barrier. When all threads arrive, a barrier action prints a message indicating that the “meeting” can start.
public class CyclicBarrierTest {
private static CyclicBarrier cyclicBarrier;
static class Worker extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName() + " arrived");
try {
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("All arrived, meeting starts...");
}
});
for (int i = 0; i < 5; i++) {
new Worker().start();
}
}
}Running the program produces output similar to:
When Does BrokenBarrierException Occur?
A BrokenBarrierException is thrown if any thread waiting at the barrier is interrupted, if another thread calls reset(), or if the barrier has already been marked broken. In all these cases the barrier transitions to a broken state and all waiting threads are released with the exception.
Key Takeaways
CyclicBarrier synchronizes a fixed number of threads, releasing them together.
It relies on ReentrantLock and Condition for thread coordination.
The barrier can be reused across multiple generations, each represented by a Generation object.
Proper handling of interruptions, timeouts, and resets is essential to avoid BrokenBarrierException.
Use the optional barrier action to perform a final computation once all parties have arrived.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
