DEV Community

Dev Cookies
Dev Cookies

Posted on

πŸ”’ CountDownLatch in Java β€” A Complete Guide with Robust Example

Java's CountDownLatch is a powerful synchronization aid often used in multithreaded systems where one or more threads must wait for a set of operations to complete. In this guide, we’ll explore:

  • What CountDownLatch is
  • How it works
  • A real-world example
  • Handling exceptions and avoiding deadlocks
  • Best practices

πŸ”§ What is CountDownLatch?

CountDownLatch is part of java.util.concurrent and helps coordinate threads by using a countdown mechanism.

πŸ“Œ Key Points:

  • Initialized with a count, representing the number of tasks to wait for.
  • Threads call await() to block until the count becomes zero.
  • Other threads call countDown() to signal task completion.
  • Not reusable β€” once the count reaches zero, it cannot be reset.

πŸ“˜ Real-World Analogy

Imagine you're launching a rocket πŸš€ but you must wait until 3 safety checks are complete. Each check is performed by a separate engineer. The launch control (main thread) waits until all checks are done.


βœ… Simple Example with Safe Error Handling

πŸ§ͺ Scenario:

  • 3 worker threads perform tasks.
  • The main thread waits for all workers to finish.
  • One worker might fail β€” how do we handle it?

🧡 Full Java Code (With Robust Handling)

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class CountDownLatchExample {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);

        Thread worker1 = new Thread(new Worker(latch, "Worker 1", false));
        Thread worker2 = new Thread(new Worker(latch, "Worker 2", true));  // Simulate failure
        Thread worker3 = new Thread(new Worker(latch, "Worker 3", false));
        Thread mainThread = new Thread(new MainThread(latch));

        worker1.start();
        worker2.start();
        worker3.start();
        mainThread.start();

        // Ensure main thread completes before JVM exits
        mainThread.join();
    }

    // Worker thread
    static class Worker implements Runnable {
        private final CountDownLatch latch;
        private final String name;
        private final boolean shouldFail;

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

        @Override
        public void run() {
            try {
                System.out.println(name + " is working...");
                Thread.sleep(2000);
                if (shouldFail) {
                    throw new RuntimeException("Simulated failure in " + name);
                }
                System.out.println(name + " finished.");
            } catch (Exception e) {
                System.err.println(name + " encountered an error: " + e.getMessage());
            } finally {
                latch.countDown(); // βœ… Always decrement latch
                System.out.println(name + " has counted down.");
            }
        }
    }

    // Main thread that waits for all workers
    static class MainThread implements Runnable {
        private final CountDownLatch latch;

        MainThread(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                System.out.println("Main thread waiting for workers to finish...");
                boolean completed = latch.await(10, TimeUnit.SECONDS); // ⏱ Timeout
                if (completed) {
                    System.out.println("βœ… All workers finished. Main thread proceeding.");
                } else {
                    System.out.println("⚠️ Timeout! Some workers did not finish in time.");
                }
            } catch (InterruptedException e) {
                System.err.println("Main thread was interrupted.");
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

πŸ›‘ What Happens If a Worker Fails Without countDown()?

If you don’t put countDown() in a finally block, and a worker throws an exception before reaching it, then:

  • The latch count won't reach zero.
  • The main thread will wait forever on await() (unless a timeout is used).
  • This causes deadlocks and unresponsive systems.

πŸ’‘ Best Practices

Scenario Best Practice
Worker may throw exception Use finally { latch.countDown(); }
Don't want to block forever Use await(timeout, TimeUnit)
Want reusable synchronization Use CyclicBarrier or Phaser instead
Need dynamic thread coordination Prefer Phaser

πŸ” Alternatives to CountDownLatch

Class Reusable? Description
CountDownLatch ❌ No One-shot latch, simple countdown
CyclicBarrier βœ… Yes All threads wait for each other
Phaser βœ… Yes Dynamic registration, flexible barrier

🧠 Summary

  • Use CountDownLatch for one-time task coordination.
  • Always use finally to ensure countDown() gets called.
  • Use await(timeout, unit) to avoid infinite waiting.
  • Consider alternatives like CyclicBarrier and Phaser for complex cases.

πŸ“Œ Suggested Interview Question

Q: What happens if a thread throws an exception before calling countDown()?
A: The latch count won’t decrement, and any thread calling await() will block forever unless a timeout is used.

Top comments (0)