CountDownLatch in Java
Complete Guide with Custom Implementation
Introduction
In multithreading, sometimes we need a way for one or more threads to wait until a set of operations in other threads completes.
This is where CountDownLatch comes in handy.
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 is part of the java.util.concurrent package.
Key Points:
- CountDownLatch uses a count initialized during construction.
- Threads wait until the count reaches zero.
- Once count reaches zero, all waiting threads proceed.
- Cannot be reused after count reaches zero.
How CountDownLatch Works
When threads call await(), they wait until the latch’s count reaches zero.
Other threads call countDown() to decrement the count.
When the count reaches zero, all waiting threads are released.
Syntax:
CountDownLatch latch = new CountDownLatch(int count);
Here:
- count = number of times countDown() must be called before threads can proceed.
Example: Using CountDownLatch
Let’s create a scenario where a main thread waits for three worker threads to finish their tasks.
Code:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int count = 3;
CountDownLatch latch = new CountDownLatch(count);
Runnable worker = () -> {
System.out.println(Thread.currentThread().getName() + " is working...");
try {
Thread.sleep((long) (Math.random() * 3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " finished work.");
latch.countDown();
};
for (int i = 0; i < count; i++) {
new Thread(worker).start();
}
System.out.println("Main thread is waiting for workers...");
latch.await();
System.out.println("All workers finished. Main thread proceeds.");
}
}
Expected Output:
Main thread is waiting for workers...
Thread-0 is working...
Thread-1 is working...
Thread-2 is working...
Thread-1 finished work.
Thread-0 finished work.
Thread-2 finished work.
All workers finished. Main thread proceeds.
Key Methods in CountDownLatch
| Method | Description |
|---|---|
| await() | Causes the current thread to wait until the latch reaches zero |
| countDown() | Decrements the count |
| getCount() | Returns the current count |
Custom Implementation of CountDownLatch in Java
Now let’s build a step-by-step custom version of CountDownLatch.
Step 1 — Plan the Logic
Our custom latch needs:
- A count variable initialized during construction.
- A mechanism to make threads wait (wait() / notifyAll()).
- A countDown() method to decrement count and notify waiting threads.
Step 2 — Create CustomCountDownLatch Class
public class CustomCountDownLatch {
private int count;
public CustomCountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count cannot be negative");
this.count = count;
}
public synchronized void await() throws InterruptedException {
while (count > 0) {
wait();
}
}
public synchronized void countDown() {
if (count > 0) {
count--;
}
if (count == 0) {
notifyAll();
}
}
public synchronized long getCount() {
return count;
}
}
Step 3 — Test CustomCountDownLatch
public class CustomCountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
int count = 3;
CustomCountDownLatch latch = new CustomCountDownLatch(count);
Runnable worker = () -> {
System.out.println(Thread.currentThread().getName() + " is working...");
try {
Thread.sleep((long) (Math.random() * 3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " finished work.");
latch.countDown();
};
for (int i = 0; i < count; i++) {
new Thread(worker).start();
}
System.out.println("Main thread is waiting for workers...");
latch.await();
System.out.println("All workers finished. Main thread proceeds.");
}
}
Expected Output
Main thread is waiting for workers...
Thread-0 is working...
Thread-1 is working...
Thread-2 is working...
Thread-1 finished work.
Thread-0 finished work.
Thread-2 finished work.
All workers finished. Main thread proceeds.
Full Code — Custom CountDownLatch Implementation in Java
// CustomCountDownLatch.java
/**
* Custom implementation of a simplified CountDownLatch in Java.
* This class allows one or more threads to wait until a set of operations
* being performed in other threads completes.
*/
public class CustomCountDownLatch {
private int count;
/**
* Constructor initializes the latch with the given count.
* @param count number of times countDown() must be called before threads can proceed
*/
public CustomCountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("Count cannot be negative");
this.count = count;
}
/**
* Causes the current thread to wait until the latch has counted down to zero.
*/
public synchronized void await() throws InterruptedException {
while (count > 0) {
wait(); // Wait until notified that count reached 0
}
}
/**
* Decrements the count of the latch.
* When count reaches zero, all waiting threads are released.
*/
public synchronized void countDown() {
if (count > 0) {
count--;
System.out.println("Latch count decreased to: " + count);
}
if (count == 0) {
notifyAll(); // Wake up all waiting threads
}
}
/**
* Returns the current count.
*/
public synchronized long getCount() {
return count;
}
}
Test Code — Using CustomCountDownLatch
// CustomCountDownLatchTest.java
public class CustomCountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
int count = 3; // Number of worker threads
CustomCountDownLatch latch = new CustomCountDownLatch(count);
Runnable worker = () -> {
System.out.println(Thread.currentThread().getName() + " is working...");
try {
// Simulate variable work time
Thread.sleep((long) (Math.random() * 3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " finished work.");
latch.countDown();
};
// Start multiple worker threads
for (int i = 0; i < count; i++) {
new Thread(worker, "Worker-" + i).start();
}
System.out.println("Main thread is waiting for workers to complete...");
latch.await(); // Main thread waits here until count reaches 0
System.out.println("All workers finished. Main thread proceeds.");
}
}
🧾 Sample Output
Main thread is waiting for workers to complete...
Worker-0 is working...
Worker-1 is working...
Worker-2 is working...
Worker-1 finished work.
Latch count decreased to: 2
Worker-0 finished work.
Latch count decreased to: 1
Worker-2 finished work.
Latch count decreased to: 0
All workers finished. Main thread proceeds.
Comparison: Custom vs Real CountDownLatch
| Feature | CustomCountDownLatch | java.util.concurrent.CountDownLatch |
|---|---|---|
| Thread Coordination | ✅ Yes | ✅ Yes |
| Resettable | ❌ No | ❌ No |
| Thread Safety | ✅ Yes (via synchronized) | ✅ Yes (via AQS) |
| Reusability | ❌ No | ❌ No |
| Fairness Control | ❌ No | ✅ Managed by AQS |
| Performance | ⚙️ Moderate (synchronized) | ⚡ High (LockSupport + AQS) |
Advantages of CustomCountDownLatch
- Understands thread coordination deeply.
- Can be modified for specific synchronization needs.
- Simple and reusable logic for learning.
Disadvantages
- No fairness control.
- Cannot reset (unlike CyclicBarrier).
- No interruption handling unless manually implemented.
Real-World Use Case
CustomCountDownLatch is useful for:
- Waiting for initialization tasks before proceeding.
- Waiting for all services to start before accepting requests.
- Coordinating parallel tasks in batch processing.
Conclusion
CountDownLatch is a powerful tool for synchronization.
The custom implementation helps understand the core logic behind it — using wait() and notifyAll() to coordinate thread execution.
In production, always use java.util.concurrent.CountDownLatch for better performance and reliability.
But building a custom version improves understanding of multithreading fundamentals.
