Command Palette

Search for a command to run...

Lesson 41.2 · Concurrency-Aware Data Structures

Coordinating Threads: Latches, Semaphores, Conditions

CountDownLatch waits for events, Semaphore limits how many threads proceed, wait/notifyAll and Condition objects let threads sleep until a state changes. BlockingQueue packages the producer-consumer pattern.

14 min

Think of it like this

A relay race: the second runner waits until the baton (a latch) arrives. A car park with 50 spaces lets cars in while the counter (a semaphore) is above zero. A waiter who sleeps until the kitchen rings the bell is using a condition.

1.Which tool

CountDownLatch(n): await() blocks until countDown() has been called n times ("wait until X happened"). Semaphore(k): acquire() takes one of k permits, release() returns it ("at most k at once", or ping-pong between threads with permits of 0 and 1).

wait / notifyAll inside synchronized, or Lock + Condition (await/signal): always wait in a while loop that re-checks the condition, because wake-ups can be spurious and another thread may have changed the state first.

BlockingQueue (ArrayBlockingQueue, LinkedBlockingQueue): put blocks when full, take blocks when empty. It's the producer-consumer pattern done for you, and the backbone of thread pools.

Main.java
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Main {
    public static void main(String[] args) throws Exception {
        BlockingQueue<Integer> q = new ArrayBlockingQueue<>(2);    // holds at most 2 items
        Thread producer = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) q.put(i);              // blocks while the queue is full
                q.put(-1);                                          // "no more items" marker
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        producer.start();
        int sum = 0;
        for (int x = q.take(); x != -1; x = q.take()) sum += x;     // blocks while empty
        producer.join();
        System.out.println("consumed sum = " + sum);
    }
}

Output

consumed sum = 15

Remember

  • Latch: wait for events. Semaphore: limit concurrency.
  • Always wait in a while loop.
  • Prefer BlockingQueue over hand-written wait/notify.

Common mistakes

  • Using if instead of while around wait().
  • notify() instead of notifyAll() when several kinds of waiters share a monitor.