CountDownLatch is a Java class defined in the package java.util.concurrent and is used when we want one or more threads to wait for an operation to complete or for other threads to finish their execution. This class was introduced in Java 5.
CountDownLatch class works just like a countdown works. It is initialized with a number, which works as the counter. When we want to decrement the counter, we can call the countdown() method, and the counter gets decremented by one. When the counter reaches the value 0, after multiple calls to countdown(), the thread is released.
But wait! When and How the thread started waiting? Do we just need to initialize the CountDownLatch class to make the thread wait? No, to make any thread with a CountDownLatch wait, we must call the await() method.
So, to setup a countdown latch with a thread, we must have a CountDownLatch in the thread, we need to initialize it with a number, equivalent to the number of steps you want the thread to wait, then call await() to make the countdown latch wait, and then use the countdown() method to decrement the counter, when the counter value reaches zero, the thread is released again.
Let's have a quick example to understand the concept:
Suppose we have a Worker.java class, which is runnable and we use a countdown latch to make it wait.
public class Worker implements Runnable{
CountDownLatch latch = null;
public Worker(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
try {
System.out.println("Inside the Worker class's run() method");
latch.await();
System.out.println("Await called on the latch...");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Worker Released");
}
}
Let's have another runnable class, Manager.java to release the countdown latch and release the Worker thread.
public class Manager implements Runnable {
CountDownLatch latch = null;
public Manager(CountDownLatch latch) {
this.latch = latch;
}
public void run() {
System.out.println("Inside the Manager class's run() method");
try {
Thread.sleep(1000);
this.latch.countDown();
System.out.println("3");
Thread.sleep(1000);
this.latch.countDown();
System.out.println("2");
Thread.sleep(1000);
this.latch.countDown();
System.out.println("1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Finally, we have the Main.java class,
public class Main {
public static void main(String arg[]) {
// initialize the countdown latch
CountDownLatch latch = new CountDownLatch(3);
Worker worker = new Worker(latch);
Manager manager = new Manager(latch);
new Thread(manager).start();
new Thread(worker).start();
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
As you can see in the Main.java class, we have initialized a CountDownLatch with a counter 3, it means we have to call the countDown() method 3 times, to release the latch. We run both the Manager and Worker threads, Worker thread starts waiting until the Manager thread calls countDown() 3 times, after which the Worker thread is released.
Features of CountDownLatch
- It can be initialized only once, and cannot be reused once it is released i.e. the count reaches zero.
- It's generally used when we have one main thread, which in turn initiates other threads and has to wait for the child threads to complete.
Practical Application
One practical usage of CountDownLatch is when in an application, we need to make multiple service calls, where all the calls are processed in separate threads, and the main application must wait until all the threads are processed completely.
In multi-threaded applications, most of the time, CountDownLatch proves to be very useful for thread management.