How do I use CountDownLatch for coordination?

A CountDownLatch is a synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. It’s initialized with a given count, and the await() methods block until the current count reaches zero due to invocations of the countDown() method.

Here is a practical guide on how to coordinate threads using CountDownLatch.

1. Basic Coordination Pattern

The most common use case is having a main thread wait for several worker threads to finish their tasks.

package org.kodejava.util.concurrent;

import java.util.concurrent.CountDownLatch;

public class LatchCoordination {
    public static void main(String[] args) throws InterruptedException {
        int numberOfWorkers = 3;
        // 1. Initialize with the number of events to wait for
        CountDownLatch latch = new CountDownLatch(numberOfWorkers);

        for (int i = 0; i < numberOfWorkers; i++) {
            new Thread(new Worker(latch, "Worker-" + i)).start();
        }

        System.out.println("Main thread is waiting...");
        // 2. Wait until the count reaches zero
        latch.await();

        System.out.println("All workers finished. Main thread proceeding.");
    }
}

class Worker implements Runnable {
    private final CountDownLatch latch;
    private final String name;

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

    @Override
    public void run() {
        try {
            System.out.println(name + " is performing work...");
            Thread.sleep(1000); // Simulate work
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 3. Crucial: Always decrement the count in a finally block
            latch.countDown();
            System.out.println(name + " finished. Remaining: " + latch.getCount());
        }
    }
}

2. Key Methods to Remember

  • new CountDownLatch(int count): Sets the initial count. This count cannot be reset. If you need to reset the count, consider using a CyclicBarrier.
  • countDown(): Decrements the count of the latch, releasing all waiting threads if the count reaches zero.
  • await(): Causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted.
  • await(long timeout, TimeUnit unit): A safer version that waits for a specific duration. It returns true if the count reached zero and false if the waiting time elapsed before the count reached zero.

3. Advanced Use: The “Starting Gun”

You can also use a CountDownLatch with a count of 1 as a “starting gun” to release many threads at the exact same moment (useful for load testing).

CountDownLatch startSignal = new CountDownLatch(1);

for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        try {
            startSignal.await(); // All threads stall here
            // Do concurrent work...
        } catch (InterruptedException e) { /* handle */ }
    }).start();
}

// All threads start simultaneously when this is called
startSignal.countDown();

Best Practices

  • Always use finally: Place countDown() inside a finally block to ensure the latch is decremented even if a worker thread encounters an exception. Otherwise, the waiting thread might block indefinitely.
  • Don’t reuse: Once a CountDownLatch reaches zero, it cannot be reused. If your algorithm requires repeating the synchronization point, use a CyclicBarrier or Phaser.
  • Check the count: You can use latch.getCount() for debugging or monitoring, but don’t use it to make control flow decisions in production code as it is volatile.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.