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.");
}
}
}
}
π 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 ensurecountDown()
gets called. - Use
await(timeout, unit)
to avoid infinite waiting. - Consider alternatives like
CyclicBarrier
andPhaser
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)