Usage of CyclicBarrier in Java Multi Threading

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:

  1. CyclicBarrier: We create a CyclicBarrier that waits for 3 threads to reach it. When all 3 threads call await(), the barrier is “tripped”, and the BarrierAction is executed, which prints a message.
  2. 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.
  3. Barrier Action: Once all threads have reached the barrier (after calling await()), the BarrierAction (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:

  1. 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.
  2. 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.
  3. 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.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *