1. Introduction
The Java concurrency API provides a CountDownLatch
class that allows one or more threads to wait until a set of operations are done.
CountDownLatch
is initialized with an integer value (N). This count is the number of operations the threads are going to wait for. When a thread wants to wait for the execution of these operations, it calls await()
method. This method causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted. When one of these operations completes, it uses the countDown()
method to decrement the internal counter of the CountDownLatch
. When the counter reaches to 0, the class wakes up all the threads that were sleeping in the await()
method.
The CountDownLatch
class has three basic elements:
- The initialization value that determines how many operations one thread has to wait for other threads to complete.
- The
await()
method causes the current thread to wait until the latch has counted down to zero. - The
countDown()
method decrements the count of the latch, releasing all waiting threads if the count reaches zero.
The typical usage of CountDownLatch
is like the following:
- main thread is created.
- main thread creates a
CountDownLatch
with some initial value. - main thread creates and starts other application threads.
- main thread calls
await()
. - Application thread runs and calls
countDown()
method to reduce the count by 1. - The count reaches 0 and the main thread passes through
await()
.
There’s no way to re-initialize the internal counter of the CountDownLatch
object or to modify its value. Once the counter is initialized, the only method you can use to modify its value is the countDown()
method. If you have any such requirement, you should use CyclicBarrier
class.
A typical use of CountDownLatch
is to divide a problem in N parts. Each smaller part of the problem will be solved with a Runnable
which will count down on the latch. When all parts of the problem are solved, the coordinating thread will be able to pass through await()
.
A CountDownLatch
initialized with a count of one serves as a simple on/off gate. In this case all threads invoking await()
wait at the gate until a thread opens the gate by invoking countDown()
.
The main purpose of CountDownLatch
latch is not to protect a shared resource. CountDownLatch
enables the main thread to wait until other threads are finished. Configuring how many number of threads a main thread should wait is very easily and is done by setting a count.
2. Example of CountDownLatch
In this example, we’ll create a CountDownLatch
initialized with value of 4. We’ll then create 4 threads. The main thread will start these threads and will call await()
on CountDownLatch
instance. The main thread will not pass through the gate until the count reaches 0. Each thread once done with the processing will reduce the count by 1. Once the count reaches 0, the main thread will pass through await()
.
import java.util.concurrent.CountDownLatch; public class CountDownLatchExample { public static void main(String args[]) { CountDownLatch countDownLatch = new CountDownLatch(4); Thread application1 = new Thread(new Application("Application1", countDownLatch)); Thread application2 = new Thread(new Application("Application2", countDownLatch)); Thread application3 = new Thread(new Application("Application3", countDownLatch)); Thread application4 = new Thread(new Application("Application4", countDownLatch)); // start threads application1.start(); application2.start(); application3.start(); application4.start(); try { // wait until countDownLatch reduces to 0. countDownLatch.await(); System.out.println("All applications started."); } catch (InterruptedException e) { System.out.println(e.getMessage()); } } } class Application implements Runnable { private String name; private CountDownLatch countDownLatch; public Application(String name, CountDownLatch countDownLatch) { this.name = name; this.countDownLatch = countDownLatch; } public void run() { try { System.out.println(name + " started and processing."); Thread.sleep(1000); } catch (InterruptedException e) { System.out.println(e.getMessage()); } System.out.println(name + " processing done."); // reduce the count of latch by 1 countDownLatch.countDown(); } }
Output
Application2 started and processing. Application4 started and processing. Application3 started and processing. Application1 started and processing. Application3 processing done. Application4 processing done. Application1 processing done. Application2 processing done. All applications started.