CountDownLatch

Definition

A synchronised count down variable that you can use to countdown in many thread.

For example we can do something like

import java.util.concurrent.CountDownLatch;

CountDownLatch countDownLatch = new CountDownLatch(5);

This will intialise our latch to be 5 to countdown. And then we can count down by doing

countDownLatch.countDown() // 4

Everytime we call countDown, the value of our CountDownLatch will minus 1 and this countDown operation is synchronised thread safe, so we can call it in any thread.

To wait for all the countDownLatch to finish (countdown == 0), we can use await()

countDownLatch.await();

This will block the current thread and wait until the CountDownLatch is 0

Use case 1: Wait until all tasks complete

For example if we have something like this:

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 100; i++) {
            executorService.submit(() -> {
                try {
                    System.out.printf("Thread %s is doing work%n", Thread.currentThread().getName());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.HOURS);
        System.out.println("All task completed");
    }
}

In here we use awaitTermination which will wait until all the thread in executorService finished. But we don't know exactly how many thread has finished and what's the progress.

We can put our result into a list of Future and iterate through it but this is not very nice:

package multithreads;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        List<Future<?>> futureResults = new LinkedList<>();

        for (int i = 0; i < 100; i++) {
            futureResults.add(executorService.submit(() -> {
                try {
                    System.out.printf("Thread %s is doing work%n", Thread.currentThread().getName());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }));
        }

        executorService.shutdown();

        for (Future<?> result : futureResults) {
            try {
                result.get();
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("All task completed");
    }
}

Using CountDownLatch

CountDownLatch is used to solve this exactly problem. The above code we can refactor to use CountDownLatch as following:

public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        final int NUM_TASKS = 100;

        CountDownLatch countDownLatch = new CountDownLatch(NUM_TASKS);
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < NUM_TASKS; i++) {
            executorService.submit(() -> {
                try {
                    System.out.printf("Thread %s is doing work%n", Thread.currentThread().getName());
                    Thread.sleep(1000);
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        executorService.shutdown();
        countDownLatch.await();
        System.out.println("All task completed");
    }
}

Use case 2: Wait and fire

Suppose we want all our thread to be ready waiting for the countdown to be complete and then all start at the same time.

This behaviour is similar to CyclicBarrier

We can do this using CountDownLatch as well:

public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        final int NUM_TASKS = 100;

        CountDownLatch countDownLatch = new CountDownLatch(NUM_TASKS);
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < NUM_TASKS; i++) {
            executorService.submit(() -> {
                try {
                    System.out.printf("Thread %s joined%n", Thread.currentThread().getName());
                    countDownLatch.await();
                    System.out.printf("Thread %s is doing work%n", Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        executorService.shutdown();

        Thread.sleep(5000);
        for (var i = 0; i < NUM_TASKS; i++) {
            countDownLatch.countDown();
        }

    }
}