CountDownLatch and CyclicBarrier in Java

 

CountDownLatch and CyclicBarrier in Java

Introduction

In concurrent programming, synchronization is crucial to ensure proper coordination between multiple threads. Java provides powerful utilities in the java.util.concurrent package to handle such synchronization. Two commonly used concurrency utilities are CountDownLatch and CyclicBarrier.

Both serve different purposes but are often confused due to their similarities. In this article, we will explore CountDownLatch and CyclicBarrier, their differences, and when to use them.


1. CountDownLatch

What is CountDownLatch?

CountDownLatch is a synchronization aid that allows one or more threads to wait until a set of operations being performed by other threads complete. It is often used to ensure that a particular task does not proceed until a given number of prerequisites have been completed.

How CountDownLatch Works

  • It is initialized with a count.

  • Threads call countDown() to decrement the count.

  • Threads waiting on await() will be blocked until the count reaches zero.

  • Once the count reaches zero, all waiting threads are released.

  • The count cannot be reset after reaching zero.

Example of CountDownLatch

Scenario: Waiting for Multiple Threads to Complete

import java.util.concurrent.CountDownLatch;

class Worker extends Thread {
    private CountDownLatch latch;

    public Worker(CountDownLatch latch, String name) {
        super(name);
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            System.out.println(getName() + " is performing a task.");
            Thread.sleep(2000); // Simulating work
            System.out.println(getName() + " has finished.");
            latch.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        
        new Worker(latch, "Worker-1").start();
        new Worker(latch, "Worker-2").start();
        new Worker(latch, "Worker-3").start();
        
        latch.await(); // Main thread waits until all workers finish
        System.out.println("All workers have finished. Main thread resumes.");
    }
}

Output:

Worker-1 is performing a task.
Worker-2 is performing a task.
Worker-3 is performing a task.
Worker-1 has finished.
Worker-2 has finished.
Worker-3 has finished.
All workers have finished. Main thread resumes.

Use Cases of CountDownLatch

  • Ensuring a service starts only after all dependencies have initialized.

  • Waiting for multiple threads to complete before proceeding.

  • Simulating multiple users performing an operation concurrently (e.g., in load testing).


2. CyclicBarrier

What is CyclicBarrier?

CyclicBarrier is another synchronization aid that allows a group of threads to wait for each other at a common barrier before proceeding further. Unlike CountDownLatch, it can be reused after the barrier is released.

How CyclicBarrier Works

  • It is initialized with a count (the number of threads required to reach the barrier).

  • Each thread calls await() to indicate it has reached the barrier.

  • Once the required number of threads has called await(), all threads are released.

  • The barrier can be reset and used again.

Example of CyclicBarrier

Scenario: Simulating a Race

import java.util.concurrent.CyclicBarrier;

class Racer extends Thread {
    private CyclicBarrier barrier;

    public Racer(CyclicBarrier barrier, String name) {
        super(name);
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try {
            System.out.println(getName() + " is ready.");
            barrier.await(); // Waiting for all racers
            System.out.println(getName() + " starts racing!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int numRacers = 3;
        CyclicBarrier barrier = new CyclicBarrier(numRacers, () -> 
            System.out.println("All racers are ready. Start the race!")
        );
        
        new Racer(barrier, "Racer-1").start();
        new Racer(barrier, "Racer-2").start();
        new Racer(barrier, "Racer-3").start();
    }
}

Output:

Racer-1 is ready.
Racer-2 is ready.
Racer-3 is ready.
All racers are ready. Start the race!
Racer-1 starts racing!
Racer-2 starts racing!
Racer-3 starts racing!

Use Cases of CyclicBarrier

  • Simulating multi-threaded tasks that need to start together.

  • Dividing a task into subtasks that need to synchronize at certain points.

  • Multiplayer gaming where players must wait until everyone is ready.


3. Differences Between CountDownLatch and CyclicBarrier

Feature CountDownLatch CyclicBarrier
Reset Capability Cannot be reset Can be reset and reused
Usage One-time use Can be used multiple times
Number of Threads Can be different each time Fixed number of threads required
When Threads Proceed When count reaches zero When all threads reach the barrier
Example Use Case Waiting for workers to complete Synchronizing multiple threads

Conclusion

Both CountDownLatch and CyclicBarrier provide powerful synchronization mechanisms for multi-threaded applications.

  • Use CountDownLatch when you need a one-time event synchronization where a thread waits for others to complete.

  • Use CyclicBarrier when you need threads to synchronize repeatedly at certain points in execution.

Understanding these utilities will help you write efficient and synchronized multi-threaded Java applications.

Previous
Next Post »