CyclicBarrier
is a synchronization aid in Java that allows a set of threads to wait for each other to reach a common execution point (barrier). Once all the threads reach this barrier, they are released simultaneously to continue their execution. It’s called “cyclic” because it can be reused after the barrier is broken.
It is commonly used in scenarios where multiple threads need to work concurrently, but occasionally they must wait for each other to complete specific tasks before moving forward.
Key Points:
- Barrier Point: A point where multiple threads stop and wait until all the threads reach this point.
- Cyclic Nature: The barrier is reusable, allowing the same
CyclicBarrier
to be used multiple times. - Parties: The number of threads that need to call the
await()
method before they can proceed.
Constructor:
CyclicBarrier(int parties)
: Creates a barrier that will trip when the given number of parties (threads) are waiting upon it.CyclicBarrier(int parties, Runnable barrierAction)
: This version allows you to specify a barrier action, which is a task that will be executed when the last thread reaches the barrier.
Example: CyclicBarrier in Action
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
// Create a CyclicBarrier for 3 threads with a barrier action
CyclicBarrier barrier = new CyclicBarrier(3, new BarrierAction());
// Create and start 3 worker threads
for (int i = 1; i <= 3; i++) {
Thread worker = new Thread(new Worker(barrier), "Worker " + i);
worker.start();
}
}
// Define a simple task to be run after all threads reach the barrier
static class BarrierAction implements Runnable {
@Override
public void run() {
System.out.println("All workers have reached the barrier, proceeding to the next phase...");
}
}
// Worker thread class
static class Worker implements Runnable {
private CyclicBarrier barrier;
public Worker(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " is performing some work...");
Thread.sleep(1000); // Simulating some work
System.out.println(Thread.currentThread().getName() + " is waiting at the barrier...");
barrier.await(); // Wait at the barrier
System.out.println(Thread.currentThread().getName() + " has crossed the barrier.");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
Explanation:
- CyclicBarrier: We create a
CyclicBarrier
that waits for 3 threads to reach it. When all 3 threads callawait()
, the barrier is “tripped”, and theBarrierAction
is executed, which prints a message. - Worker Threads: Each worker thread simulates some work by sleeping for 1 second. After finishing the work, each thread calls
barrier.await()
, waiting for other threads to reach the same point. - Barrier Action: Once all threads have reached the barrier (after calling
await()
), theBarrierAction
(new BarrierAction()
) runs, indicating that all threads can proceed to the next phase.
Output (Sample):
Worker 1 is performing some work...
Worker 2 is performing some work...
Worker 3 is performing some work...
Worker 1 is waiting at the barrier...
Worker 2 is waiting at the barrier...
Worker 3 is waiting at the barrier...
All workers have reached the barrier, proceeding to the next phase...
Worker 1 has crossed the barrier.
Worker 2 has crossed the barrier.
Worker 3 has crossed the barrier.
Use Cases:
- Multistage Computations: When you have a large problem that can be broken down into smaller tasks, you can use
CyclicBarrier
to synchronize threads at various stages of the computation before moving to the next step. - Simulations: In simulations (e.g., traffic, physics, game loops), threads often need to pause and wait for other threads to finish their part of the simulation before moving to the next phase.
- Parallel Data Processing: In scenarios where multiple threads process parts of a dataset in parallel and then need to combine their results, a
CyclicBarrier
can ensure that all threads reach a synchronization point before proceeding with result combination.
CyclicBarrier vs CountDownLatch:
- CyclicBarrier is reusable, and its primary use is for synchronizing threads that must wait for each other to reach a common point before continuing. It is “cyclic” because you can reuse it for multiple phases of synchronization.
- CountDownLatch is a one-time-use latch that counts down to zero, where threads can wait until the count reaches zero but does not require all threads to wait simultaneously. Once the count reaches zero, it cannot be reset.
Common Exceptions:
- InterruptedException: Thrown when a thread is waiting at the barrier and is interrupted before all the parties have arrived.
- BrokenBarrierException: Thrown when one of the threads breaks the barrier (e.g., by not reaching the barrier due to an exception or timeout).
CyclicBarrier simplifies synchronization in complex multi-threaded programs by allowing multiple threads to meet at a barrier point, synchronize their progress, and proceed together to the next phase of the program.