CountDownLatch Class - Multithreading Java

CountDownLatch Class - Multithreading Java

Java being a multi-threaded programming language, that gives you the ability to build multithreaded programs. In other words, programs that contain two or more parts that can run concurrently and each part can handle a different task at the same time, resulting in a much quicker program execution and an optimal use of the available resources specially when your computer has multiple CPUs. Those parts that can run concurrently, they are nothing but Threads.

For the simplicity of this article, I'll just give to a quick guide to the CountDownLatch class , demonstrate how it can be used in code and compare it with thread's join() method. However, I assume or recommend you to have basics on multi-threading concept to understand the content of this article. For this reason, If you need a quick brush up on it, click here or use any other resource.

Java - CountDownLatch class

CountDownLatch is a high-level synchronization utility which you can use to block a thread until other threads are ready or have completed a given task. This is achieved by a countdown.

working of CountDownLatch:

To demonstrate this, I have my class CountDownLatchDemo with a main method to invoke the demo() method. Also note that I have added comment to each line to better explain what is happening.

Basically inside demo(), I created a CountDownLatch object with a count of 3. Also, I created 2 threads and to make this simpler, I'll name the thread that needs to wait a waiter and the other thread that executes a given task from start to end without interruption a decrementer. Finally, I'll start waiter execution before decrementer.

public class CountDownLatchDemo {

    public static void main(String[] args){
        CountDownLatchDemo countDownLatchDemo = new CountDownLatchDemo();
        countDownLatchDemo.demo();
    }
     public void demo() {
        //Create a CountDownLatch instance 
        CountDownLatch latch = new CountDownLatch(3);

        //Create a Waiter thread instance 
        //which takes a CountDownLatch instance as a param
        Waiter waiter = new Waiter(latch);

        //Create a Decrementer thread instance 
        //which takes a CountDownLatch instance as a param
        Decrementer decrementer = new Decrementer(latch);

        //Start waiter thread execution
        //Before decrementer thread
        new Thread(waiter).start();

        //Start decrementer thread execution
        //After waiter thread
        new Thread(decrementer).start();
    }
}

Let's take a look also what is a Waiter and Decrementer:

 //Waiter is a Thread as it implements Runnable
    class Waiter implements Runnable{
        //CountDownLatch instance is an attribute of Waiter
        CountDownLatch latch = null;

        //constructor 
        public Waiter(CountDownLatch latch) {
            this.latch = latch;
        }

        //Override run() which is 
        // invoked once we call new Thread(waiter).start()
        public void run() {
            try {
                // we block waiter thread from running 
                // until other threads countdown reaches zero. 
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //This runs once waiter is Released to execute     
            System.out.println("Waiter Released");
        }
    }

    //Waiter is a Thread as it implements Runnable
    class Decrementer implements Runnable {
        //CountDownLatch instance is an attribute of Waiter
        CountDownLatch latch = null;

        //constructor 
        public Decrementer(CountDownLatch latch) {
            this.latch = latch;
        }

        //Override run() which is 
        // invoked once we call new Thread(waiter).start()
        public void run() {
            try {
                //Wait for 1 second and run 
                Thread.sleep(1000);
                //make a countdown on the from 3 to 2
                this.latch.countDown();
                System.out.println("latch.countDown() 1st Time");

                //Wait for 1 second and run 
                Thread.sleep(1000);
                //make a countdown on the from 2 to 1
                this.latch.countDown();
                System.out.println("latch.countDown() 2nd Time");

                //Wait for 1 second and run 
                Thread.sleep(1000);
                //make a countdown on the from 1 to 0
                this.latch.countDown();
                System.out.println("latch.countDown() 3rd Time");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

Now, If we run this, as soon as Waiter's run() method gets invoked, we expect that waiter thread to get blocked because we called latch.await() on it. And the only way for it to continue execution is when the same instance latch (CountDownLatch instance) counts down to zero. On the other hand, when Decrementer's run() method gets invoked, decrementer thread execution won't be interrupted and more over, decrementer thread is calling latch.countDown() which decrements the count value by 1 each time to the same instance of CountDownLatch that waiter thread is waiting on. As a result, the output will look as follows:

CountDownLatch_Ooutput.png

Thread's join() vs CountDownLatch

Thread join method similarly waits for other thread to finish before further executing current thread. For instance, If we have a main thread running and for some other thread t1, we invoke t1.join() in the main thread, then main thread will wait at that point until t1 finishes its job.

Let's break down the difference comparing to what we saw with CountDownLatch

  1. Thread's join() method wait for joined thread to finish the execution. In this case t1, before the main thread on which join method is called to start or complete its execution. Whereas in CountDownLatch, latch.await() does not wait for the thread that calls latch.countDown() to be finished, it proceeds once the counter value reaches 0 and it has no association with the state of the thread that calls countDown() which is decrementer in our example.

  2. We can call join() method when we have control over the threads, but in some cases like when using ExecutorService to schedule tasks execution order, we don't have control over individual threads instead we deal with just submitting the task to framework and it internally manages threads in this situation using CountDownLatch is right approach.

Conclusion

CountDownLatch is a very convenience class to do synchronization between two or more threads, it can save your time in suitable cases and you have to be aware of this class. But as you probably know, always synchronization of threads can raise deadlocks if your code is not good. That's why developers must be very careful to avoid such situation because concurrency use cases can be very complex. In fact, I recommend you to read this example of a deadlock while using CountDownLatch here. Finally, I challenge you to solve problem 1114 on leetcode if you think that you gained something from this article.