How does CountDownLatch works in Java?

6.8k views Asked by At

I am studying Synchronization in Java. I am not able to understand the exact mechanism of CountDownLatch.

Does CountDownLatch 'counts down the latch' (waits for completion of number of threads) as per the number of threads which are given at declaration?

Here is the code I tried to understand:

public class LatchExample implements Runnable {
    private CountDownLatch latch;

    private int id;

    public LatchExample(int id, CountDownLatch latch){
        this.id=id;
        this.latch = latch;
    }

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(5);

        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 7; i++) {
            executor.submit(new LatchExample(i,latch));
        }

        try {
            latch.await();
            System.out.println("all process completed");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        System.out.println();
    }

    @Override
    public void run() {

        System.out.println("Starting: "+id);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        latch.countDown();
    }
}

In the example above:

7 threads are spawned by ExecutorService (from the Thread pool). My understanding is that the latch should wait for completion of 6 threads (from 0 to 5), as defined by:

CountDownLatch latch = new CountDownLatch(5);

But the output that I get is not constant every time. Sometimes it waits for 6 threads to complete and sometimes it waits for 7 e.g.:

Starting: 1
Starting: 0
Starting: 2
Starting: 3
Starting: 5
Starting: 4
Starting: 6
all process completed

Here is output at alternate times:

Starting: 0
Starting: 2
Starting: 1
Starting: 4
Starting: 5
Starting: 3
all process completed

Starting: 6

EDIT : The CountDownLatch should ideally countDown until 5 tasks are passed the latch. Here it is showing as either 6 or 7.

What would be the fix for the code, if I want it to always display only 5 tasks before 'all process completed' ?

6

There are 6 answers

0
AudioBubble On

A CountDownLatch allows a thread to pass once you countDown all of the permits available in the latch.

In this case, since you have 5 permits, but 7 threads, the 5th thread will unlock the latch, and the program continues. But, you still have 2 threads left.

Since the latch was unlocked, the program continues without the last 2 threads, because there are no permits left to countDown. To fix this, just have 7 permits instead of 5.

0
outdev On

CountDownLatch is initialized with the number, This number means how many times countDown() must be called, before threads waiting on await() can procced.

new CountDownLatch(5) 

countDown() must be called 5 times, and only then the threads waiting on await() can procced. So change your code to

new CountDownLatch(7) 
0
BillRobertson42 On

It's independent of threads.

When somebody calls await on a countdown latch, they will block if the counter is not zero. They will continue to block until the counter reaches zero, or until they hit the timeout (if they called the version of await that lets you timeout).

You could have one thread doing work, and decrementing the counter and another thread waiting for those n things to be done. You could also have a countdown latch with a value of 1, and let another thread do initialization while building the gui. Then have the main thread block on the latch before making the gui visible. There are lots of possibilities.

0
Marko Topolnik On

Your latch will need five countDown() calls to reach zero and let await return. However, the way your task for the executor is written, more tasks will start running than needed to release the latch. The most helpful approach to seeing where you are with your code is working through a run step by step, like this:

  1. tasks 0-2 start running on the three threadpool threads;
  2. you see Starting: n printed three times, where n ranges between 0 and 2, but in arbitrary order;
  3. a second passes;
  4. the three tasks complete almost simultaneously, bringing the latch's count down to 2;
  5. tasks 3-5 start running;
  6. you see Starting: n printed three times, n ranging between 3 and 5, in arbitrary order;
  7. another second passes;
  8. tasks 3-5 complete almost simultaneously, releasing the latch. Now both the main thread can continue and task 6 can start;
  9. all processes completed prints almost at the same time as Starting: 6, in arbitrary order.

Now, I am not quite clear what you expected your code would do, but I hope the above way of reasoning will help you bring it to a state where its behavior aligns with your expectation.

0
adhg On

I think the best way to understand the mechanism behind countDownLatch is to have an analogy, so my 2 cents goes like this: think of a disco party and you're the DJ you wont put your favorite song named "all process completed" until you counted 5 people on the dance floor. Every time someone comes in, you count down (from 5) and when you reach 0 - you put your favorite song. You don't care if they are all together on the dance floor, all you care that you counted 5 people on the dance floor.

Now, for your example, you put countDownLatch=5 and your for loop has <7 (that's ok). So when 5 dancers comes in - you put the song "all process completed"; so the result are just ok.

you asked:

Does CountDownLatch 'counts down the latch' (waits for completion of number of threads) as per the number of threads which are given at declaration?

No, it doesn't wait for the thread to complete, it doesn't care about the thread at all, it just care about your count.

In the real world, you will use this mechanism when you want to ensure that all threads have preformed a specific task and now you're READY to continue with main (or a different thread).

0
Nasreen On

Eventhough my answer is too late, hope this helps to some one like me who are new to threads. As per my analysis, countdown latch works as it is intended. Please do this change and see the output

 @Override
 public void run() {

    //System.out.println("Starting: "+id);
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    latch.countDown();
    System.out.println("Finished the thread: "+id);
}