Java Coordinating Threads with a CountDownLatch
A CountDownLatch is a gate that opens when a fixed count of events has happened. Threads wait on the latch with await(), and other threads drop the count with countDown().
Workers count down when done
Three workers each store a word in their own slot. Each worker counts the latch down after storing its word.
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws Exception {
String[] words = new String[3];
CountDownLatch done = new CountDownLatch(3);
String[] tasks = {"venue", "stage", "lights"};
for (int i = 0; i < tasks.length; i++) {
int slot = i;
new Thread(() -> {
words[slot] = tasks[slot];
done.countDown();
}).start();
}
done.await();
for (String word : words) {
System.out.println(word);
}
}
}A latch as a starting gate
The gate latch starts at one, so every worker waits on it. The main thread opens the gate, and a second latch confirms all three workers finished.
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws Exception {
String[] reports = new String[3];
CountDownLatch gate = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
int slot = i;
new Thread(() -> {
try {
gate.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
reports[slot] = "Worker " + (slot + 1) + " finished";
done.countDown();
}).start();
}
gate.countDown();
done.await();
for (String report : reports) {
System.out.println(report);
}
}
}Example
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws Exception {
String[] words = new String[3];
CountDownLatch gate = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(3);
String[] tasks = {"cables", "speakers", "microphones"};
for (int i = 0; i < tasks.length; i++) {
int slot = i;
new Thread(() -> {
try {
gate.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
words[slot] = tasks[slot];
done.countDown();
}).start();
}
gate.countDown();
done.await();
for (String word : words) {
System.out.println(word);
}
System.out.println("All 3 workers finished");
}
}Key points
- A CountDownLatch starts with a fixed count, set when the latch is created.
- Each countDown() call drops the count by one.
- await() blocks the calling thread until the count reaches zero.
- Worker threads store results in their own slots and never print.
- The main thread prints the slots in order after await() returns.
- A CyclicBarrier is the reusable form of a latch. It resets after each meeting.
- A Semaphore is the permit form. It limits how many threads pass at once.
In this example, three workers wait on a closed gate latch. The main thread opens the gate with one countDown() call. Each worker stores its word in its own slot and counts the done latch down. The main thread prints the words in slot order after the done latch reaches zero.
Note
A latch cannot be reset. A new latch is needed for a new round.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A CountDownLatch has reached zero and a new round of work begins. What must the program do?