NOTE

6.30 Producer-Consumer

Producer-consumer coordination with BlockingQueue, wait/notify, and Condition, including bounded capacity and correct wait-loop semantics.

JavaCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. Prefer BlockingQueue for the Common Case

Producer-consumer is fundamentally a coordination and backpressure problem. A bounded BlockingQueue directly models both:

BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(100);

// producer
queue.put(value);

// consumer
Integer value = queue.take();

A bounded queue prevents producers from building an unlimited in-memory backlog when consumers are slower.

SynchronousQueue is a special zero-capacity handoff: every put rendezvous with a take.

2. wait / notifyAll

The lower-level monitor pattern is:

synchronized (lock) {
    while (queue.isEmpty()) {
        lock.wait();
    }
    Object value = queue.remove(0);
    lock.notifyAll();
}

Always re-check the condition in a while loop because wake-up does not imply that the condition is still true when the thread reacquires the monitor.

For multiple producers/consumers, notifyAll() is usually easier to make correct than trying to infer which single waiter should be awakened.

3. Lock + Condition

A ReentrantLock can expose separate conditions such as notEmpty and notFull. This lets producers wait specifically for capacity and consumers wait specifically for data.

The same rule applies: await in a loop, update shared state while holding the lock, then signal the relevant condition.

4. Choosing the Abstraction

Use BlockingQueue unless you specifically need a custom synchronization protocol. It packages the difficult visibility, locking, waiting, and wake-up rules into a tested higher-level abstraction.

Loading helpful count