Java Concurrency CountDownLatch Tutorial

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.


Leave a Reply

Your email address will not be published.