Table of Contents
Overview
CountDownLatch, as the name suggests, it provides a countdown mechanism. One use case of this class is to block the calling thread until all other specified threads are complete.
It sounds vague, doesn’t it? Let’s consider one concrete example to boost our understanding.
CountDownLatch Mechanism
The principle of CountDownLatch is you create a countdown with an initial number. Every time a task is complete (a Runnable, for example), you call the method countdown
on the latch to decrease the number by one.
On the calling thread, you call the await
function. Only when the countdown reaches 0, the await
function returns and you can continue the task on the calling thread.
Real-life CountDownLatch example
Let’s say you and three other friends go out camping. It’s time for a meal and you want to cook a pot of soup. In order to cook this soup, you need:
- Water
- Vegetables
- Meat
You cannot cook until you have all ingredients. Thus, you assign three friends three tasks
- one to fetch water
- one to get vegetables
- one to get some meat
You need to wait for all of your friends to bring the ingredients back. This is a candidate for using CountDownLatch.
Let’s convert this story into a video. You are the yellow rectangle. Other friends are three circles.
CountDownLatch code implementation
Let’s create a Runnable class simulating a task:
class FetchingTask implements Runnable { private final String taskName; private final CountDownLatch latch; public FetchingTask(String taskName, CountDownLatch latch) { this.taskName = taskName; this.latch = latch; System.out.println("Start doing " + taskName); } @Override public void run() { try { Thread.sleep(new Random().nextInt(5000) + 3000); } catch (InterruptedException ex) { // } System.out.println("Task " + taskName + " is done"); latch.countDown(); } }
As you can see, in the run
method, the thread waits for a random number of seconds between 5 and 8. When the wait is done, the countdown
method is called on the shared CountDownLatch instance.
Now, let’s implement the story above:
public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(3); System.out.println("We need some ingredients, please go fetch!"); var executors = Executors.newFixedThreadPool(3); executors.submit(new FetchingTask("Fetching water", latch)); executors.submit(new FetchingTask("Fetching vegetables", latch)); executors.submit(new FetchingTask("Fetching meat", latch)); latch.await(); System.out.println("Now we cook!"); executors.shutdown(); }
In the main thread, we created a CountDownLatch and specified the counter with value = 3 (because we have 3 tasks).
Next, we submitted the tasks to an executor and call latch.await()
to wait for the countdown to reach 0.
Let’s see how the application run:
As you can see, once all the fetching tasks are complete, the main thread continues.
Countdown never reaches 0
Imagine the guy who went to fetch meat was super ambitious. He wanted to hunt a tiger and ended up in its belly. Thus, the countdown never reaches 0 and the main thread will wait forever. That means three of you can never eat the soup and also end up starving.
We don’t want that. If you don’t want that either, you need to specify how long you will wait. For example, you only wait for 10 seconds. Let’s try the overloaded version of await
var result = latch.await(10, TimeUnit.SECONDS); if (!result) { System.out.println("We don't have all ingredients but we cook anyway"); } else { System.out.println("Now we cook!"); }
Conclusion
Now you know how to use CountDownLatch in your program. To recap, you use this class to initiate a countdown and let the calling thread wait until the countdown reaches 0. To decrease the countdown, you call countdown
on the latch instance. Normally, you would call this in threads you want to wait.
I build softwares that solve problems. I also love writing/documenting things I learn/want to learn.