Monday, July 19, 2021

How to use CountDownLatch in Java? Example

CountDowaLatch is a high-level synchronization utility that is used to prevent a particular thread to start processing until all threads are ready. This is achieved by a countdown. The thread, which needs to wait for starts with a counter, each thread make the count down by 1 when they become ready, once the last thread call countDown() method, then the latch is broken and the thread waiting with the counter starts running. CountDownLatch is a useful synchronizer and is used heavily in multi-threaded testing.

 You can use this class to simulate truly concurrent behavior i.e. trying to access something at the same time once every thread is ready. Worth noting is that CountDownLatch starts with a fixed number of counts which cannot be changed later, though this restriction is re-mediated in Java 7 by introducing a similar but flexible concurrency utility called Phaser.

There is another similar utility called CyclicBarrier, which can also be used in this situation, where one thread needs to wait for other threads before they start processing. The only difference between CyclicBarrier and CountDownLatch is that you can reuse the barrier even after its broker but you cannot reuse the count-down latch, once the count reaches zero.

Mastering Concurrency is not easy but if you seriously want to become an expert Java programmer, you got to tame this horse. One thing which can help you in your journey is the Brian Goetz classic, Java Concurrency in Practice. One of the most recommended books for Java programmers.




How to use CountDownLatch in Java? Example

The theory is easy to read but you cannot understand it until you see it live in action, this is even more true with concurrency and multi-threading. So let's see how to use CountDownLatch in Java with a simple demo. In this example, we have the main thread that is required to wait until all worker thread finished their task. 

In order to achieve this, I have created a CountDownLatch with a number of counts equal to 4, which is the total number of worker threads. I then passed this CountDownLatch to each worker thread, whenever they complete their task, they call the countDown() method, once the last worker thread calls the countDown() method then the latch is broken and the main thread which has been waiting on the latch start running again and finished its execution.

In order to truly understand this problem, you first need to run by commenting latch.await() call in the main method, without this call main thread will not for any worker thread to finish their execution and it will terminate as soon as possible, maybe even before any worker thread gets started. If you run again by an un-commenting latch.await() then you will always see that the main thread has finished last. Why? because await() is a blocking call and it blocks until the count reaches zero.

Also, you cannot reuse the latch once count=0, calling await() method on the latch will have no effect i.e. thread will not block, but they not throw any exceptions as well, which is a little bit counterintuitive if you are expecting an IllegalThreadStateException.

You can also check these advanced Java multithreading and concurrency courses to learn more about these higher-level concurrency utilities introduced on Java 1.5 release.

How to use CountDownLatch in Java? Example


Here is the complete program and code to use CountDownLatch in Java :

import java.util.concurrent.CountDownLatch;

/**
 * Java Program to demonstrate how to use CountDownLatch, 
 * Its used when a thread
 * needs to wait for other threads before starting its work.
 * 
 * @author Javin Paul
 */
public class CountDownLatchDemo {

    public static void main(String args[]) throws InterruptedException {
        
        CountDownLatch latch = new CountDownLatch(4);
        Worker first = new Worker(1000, latch, "WORKER-1");
        Worker second = new Worker(2000, latch, "WORKER-2");
        Worker third = new Worker(3000, latch, "WORKER-3");
        Worker fourth = new Worker(4000, latch, "WORKER-4");
        
        first.start();
        second.start();
        third.start();
        fourth.start();
        
        // Main thread will wait until all thread finished
        latch.await();
        
        System.out.println(Thread.currentThread().getName() 
                      + " has finished");

    }

}

class Worker extends Thread {
    private int delay;
    private CountDownLatch latch;

    public Worker(int delay, CountDownLatch latch, String name) {
        super(name);
        this.delay = delay;
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(delay);
            latch.countDown();
            System.out.println(Thread.currentThread().getName() 
                         + " has finished");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Output
WORKER-1 has finished
WORKER-2 has finished
WORKER-3 has finished
WORKER-4 has finished
main has finished

If you call latch.await() again, after the count reaches zero, it will not stop the main thread again, neither it will throw an Exception, it will just pass through it, as shown below :

// Main thread will wait until all thread finished
latch.await();
        
System.out.println(Thread.currentThread().getName() 
       + " has started running again");
        
latch.await();
System.out.println(Thread.currentThread().getName() + " has finished");

Output
main has started running again
main has finished

This is where CountDownLatch is different than CyclicBarrier because you can reuse the barrier to stop the thread even after the barrier is broken.



Important points about CountDownLatch in Java

Let's revisit some important things about CountDownLatch in Java. This will help you to retain the knowledge you have just learned :

1. When you create an object of CountDownLatch you pass an int to its constructor (the count), this is actually the number of invited parties (threads) for an event.

2. The thread, which is dependent on other threads to start processing, waits on the latch until every other thread has called count down. All threads, which are waiting on await() proceed together once count down reaches zero.

3. The countDown() method decrements the count and await() method blocks until count == 0

4. Once the count reaches zero, the countdown latch cannot be used again, calling await() method on that latch will not stop any thread, but it will neither throw any exception.

5. One of the popular use of CountDownLatch is in testing concurrent code, by using this latch you can guarantee that multiple threads are firing requests simultaneously or executing code at almost the same time.

6. There is a similar concurrency utility called CyclicBarrier, which can also use to simulate this scenario but the difference between CountDownLatch and CyclicBarrier is that you can reuse the cyclic barrier once the barrier is broken but you cannot reuse the CountDownLatch one count reaches to zero.

7. Java 7 also introduced a flexible alternative of CountDownLatch, known as Phaser. It also has a number of unarrived parties just like barriers and latches but that number is flexible. So it's more like you will not block if one of the guests bunks the party.


That's all in this Java CountDownLatch example. In this tutorial, you have learned how to use CountDownLatch in Java to make sure your key thread starts processing once all pre-conditions are full-filled or when all threads are ready. 

There are two other similar utilities, one is called Phaser, and the other is called CyclicBarrier, if you want a dynamic count, go for Phaser and if you want to reuse the barrier, again and again, go for CyclicBarrier. We will see examples of these two synchronizers in Java in our next Java Concurrency tutorial.

6 comments:

  1. Here you can find my article where in the example code you can see use of CountDownLatch to wait for specific websocket messages. Maybe someone will find it useful.
    http://blog.mwrobel.eu/activiti-bpmn-engine-events-via-websockets/?lang=en

    ReplyDelete
  2. if You write -

    public void run() {
    try {
    // Thread.sleep(delay);
    System.out.println(Thread.currentThread().getName() + " has started");
    latch.countDown();
    System.out.println(Thread.currentThread().getName() + " has finished");
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    statement for "started" for all workers should be printed before "finished" statement. But it's not happening in above example.

    ReplyDelete
    Replies
    1. Since latch.await() is being invoked from inside the main() method means it's main() method which awaits count to become zero. And not the workers thread which have to await. They just invoke countDown() method to decrease the count by 1 for each invocation. Hence the Worker threads are free to complete and thus printing "finished". Hope this answers your question.

      Delete
  3. please correct the starting line as CountDownLatch. It is mentioned as CountDowaLatch

    ReplyDelete
  4. If the delay for each worker thread is kept the same

    WorkerCDL first = new WorkerCDL(1000, cdl, "WORKER-1");
    WorkerCDL second = new WorkerCDL(1000, cdl, "WORKER-2");
    WorkerCDL third = new WorkerCDL(1000, cdl, "WORKER-3");
    WorkerCDL fourth = new WorkerCDL(1000, cdl, "WORKER-4");

    Following is the output

    Thread WORKER-1 has finished
    Thread WORKER-2 has finished
    Thread WORKER-4 has finished
    Thread main has finished
    Thread WORKER-3 has finished

    does it imply that the main thread is not waiting for the countdown to reach 0 before proceeding?

    ReplyDelete
    Replies
    1. No, main thread will only continue after all thread called the countDown() method i.e. when count reaches zero. It's just that print statement comes after calling countDown() method and that delay is enough for main thread to proceed. If you put the print statement above calling countDown() method in run() method of Worker thread, you will see the output you want.

      Delete

Feel free to comment, ask questions if you have any doubt.