Producer-Consumer Problem in Java
Problem
In the producer-consumer problem one thread creates items and another processes them through a shared buffer of limited size; the producer must wait when it's full and the consumer when it's empty.
Have a producer thread create the items 1 to 5 and a consumer thread process them, using a buffer that holds at most two items.
Java Program
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ProducerConsumerQueue {
private static final int DONE = -1; // a "poison pill" telling the consumer to stop
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(2); // holds at most 2 items
Thread producer = new Thread(() -> {
try {
for (int item = 1; item <= 5; item++) {
queue.put(item); // waits if the queue is full
}
queue.put(DONE);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
while (true) {
int item = queue.take(); // waits if the queue is empty
if (item == DONE) break;
System.out.println("Consumed " + item);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
producer.join();
consumer.join();
System.out.println("All items processed");
}
}Output
Core Logic
A BlockingQueue handles all the waiting: put pauses the producer when the queue is full, and take pauses the consumer when it's empty.
- 1
new ArrayBlockingQueue<>(2)is the shared, bounded buffer. - 2The producer calls
queue.put(item)for 1 to 5, then puts the special valueDONE. - 3The consumer loops on
queue.take(), printing each item until it receivesDONE. - 4Main joins both threads before printing the final message.
put until the consumer frees a slot — yet items always come out in order 1 to 5.Key Point: The "poison pill" (DONE = -1) is a clean way to tell a consumer that no more work is coming, without interrupting it.
Key Concepts
Approach 2: Using wait() and notifyAll()
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumerWaitNotify {
private final Queue<Integer> buffer = new LinkedList<>();
private final int capacity = 2;
synchronized void put(int item) throws InterruptedException {
while (buffer.size() == capacity) {
wait(); // releases the lock and sleeps until notified
}
buffer.add(item);
notifyAll(); // wake any waiting consumer
}
synchronized int take() throws InterruptedException {
while (buffer.isEmpty()) {
wait();
}
int item = buffer.remove();
notifyAll(); // wake any waiting producer
return item;
}
public static void main(String[] args) throws InterruptedException {
ProducerConsumerWaitNotify shared = new ProducerConsumerWaitNotify();
Thread producer = new Thread(() -> {
try {
for (int item = 1; item <= 5; item++) shared.put(item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) System.out.println("Consumed " + shared.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
producer.join();
consumer.join();
System.out.println("All items processed");
}
}
Output
Core Logic
The classic low-level version builds the bounded buffer by hand, using an object's monitor to make threads wait and wake each other.
- 1
putandtakearesynchronizedon the shared object. - 2
putcallswait()while the buffer is full;takecallswait()while it's empty. - 3After changing the buffer, each method calls
notifyAll()so a waiting thread can re-check its condition. - 4The consumer takes exactly five items, so no poison pill is needed.
wait() until the producer's notifyAll() wakes it.Key Point: Always call wait() inside a while loop, not an if — a woken thread must re-check the condition, because another thread may have changed it first.
Key Concepts
Interview Questions
What happens if the consumer is slower than the producer?
The bounded queue fills up and put blocks the producer until there's space, which naturally slows production to match.
How would you support several consumers?
Start more consumer threads on the same queue and send one poison pill per consumer, so each one stops.