DEV Community

Dev Cookies
Dev Cookies

Posted on

🚧 Mastering `CyclicBarrier` in Java with Real-World Code Examples

Concurrency in Java can be tricky, but Java’s java.util.concurrent package provides powerful utilities to handle multithreading gracefully. One such utility is the CyclicBarrier β€” a synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.


πŸ” What is CyclicBarrier?

CyclicBarrier is used to make threads wait at a common barrier point. Once all threads have reached the barrier, they are released to continue execution. It's called cyclic because it can be reused after the waiting threads are released β€” unlike CountDownLatch which cannot be reset.


🧠 When to Use CyclicBarrier?

Use CyclicBarrier when:

  • You want multiple threads to perform independent tasks and wait for others before continuing.
  • You have a multi-phase task, where threads need to synchronize after each phase.
  • You are simulating a game round, parallel data processing, or distributed computation steps.

βœ… Real-World Example: Worker Threads Synchronizing Before Task Execution

Let’s imagine you have 3 worker threads that perform some setup, then must all wait until everyone is ready before starting the main task.


πŸ’‘ Step-by-step Breakdown:

  1. Create a CyclicBarrier with a count of 3.
  2. Each thread does its preparation work.
  3. Each thread then calls await() to wait for others.
  4. When all threads reach the barrier, a barrier action runs (optional).
  5. All threads proceed with the main task.

πŸ§ͺ Code Snippet

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {

    // Barrier for 3 threads with a barrier action
    private static final CyclicBarrier barrier = new CyclicBarrier(3, () -> {
        System.out.println("\nβœ… All workers reached the barrier. Let's start the main task!\n");
    });

    static class Worker implements Runnable {
        private final String name;

        Worker(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            try {
                System.out.println(name + " πŸ› οΈ is doing preparation...");
                Thread.sleep((long) (Math.random() * 3000));

                System.out.println(name + " ⏸️ waiting at the barrier...");
                barrier.await();  // Wait for others

                System.out.println(name + " πŸš€ starting the main task.");
            } catch (InterruptedException | BrokenBarrierException e) {
                System.err.println(name + " ❌ error: " + e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        // Launch 3 worker threads
        new Thread(new Worker("Worker-1")).start();
        new Thread(new Worker("Worker-2")).start();
        new Thread(new Worker("Worker-3")).start();
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ–₯️ Sample Output

Worker-2 πŸ› οΈ is doing preparation...
Worker-3 πŸ› οΈ is doing preparation...
Worker-1 πŸ› οΈ is doing preparation...
Worker-1 ⏸️ waiting at the barrier...
Worker-2 ⏸️ waiting at the barrier...
Worker-3 ⏸️ waiting at the barrier...

βœ… All workers reached the barrier. Let's start the main task!

Worker-2 πŸš€ starting the main task.
Worker-1 πŸš€ starting the main task.
Worker-3 πŸš€ starting the main task.
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Reusability of CyclicBarrier

The same barrier can be reused by calling await() again. If your task has multiple stages (e.g., MapReduce phases), this becomes incredibly powerful.


⚠️ Exception Handling Tips

  • BrokenBarrierException: Thrown if a thread is interrupted while waiting or if another thread leaves the barrier prematurely.
  • Always wrap await() in a try-catch block.

🧭 Comparison with CountDownLatch

Feature CyclicBarrier CountDownLatch
Reusable? βœ… Yes ❌ No
Barrier Action? βœ… Yes (optional Runnable) ❌ No
Usage Style All threads wait One or more threads wait for others to finish

πŸ›  Real Use Cases

  • Game engines: Wait for all players to be ready before starting a round.
  • Simulations: Synchronize steps in time-stepped simulations.
  • Parallel computing: Wait for all worker threads to complete a phase before starting the next.

🧡 Pro Tip

If you have a dynamic number of threads, CyclicBarrier might not be ideal. In such cases, consider alternatives like Phaser.


πŸ“š Conclusion

CyclicBarrier is an elegant solution for coordinating multiple threads that must wait for each other before proceeding. It's easy to implement and highly useful in collaborative parallel processing tasks.


πŸ“Œ What's Next?

  • πŸ”„ Try using CyclicBarrier for multiple phases
  • πŸ”€ Combine with ExecutorService for cleaner thread management
  • βš™οΈ Explore Phaser for more dynamic thread coordination

Top comments (0)