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();
}
}
}