Producer–Consumer Problem in Java Using BlockingQueue
The Producer–Consumer problem is one of the most important multi-threading patterns in Java. Traditionally, developers used wait and notify to coordinate threads, but this approach is complex and error-prone. Modern Java provides a cleaner solution: BlockingQueue.
1. What is the Producer–Consumer Problem?
Two types of threads share a common buffer:
- Producer → Generates data and puts it into the buffer
- Consumer → Retrieves data from the buffer and processes it
The challenge: prevent producers from writing when the buffer is full and consumers from reading when the buffer is empty.
2. Problems with wait()/notify()
The older approach required:
- Manual locking
- Complex condition handling
- Risk of deadlocks
- Hard to debug
BlockingQueue solves these issues with built-in thread management.
3. Why BlockingQueue?
BlockingQueue handles synchronization internally. Its key methods:
- put() – waits if the queue is full
- take() – waits if the queue is empty
No need for wait(), notify(), or synchronized blocks.
4. Example Implementation (Producer + Consumer)
import java.util.concurrent.*;
public class ProducerConsumerDemo {
public static void main(String[] args) {
BlockingQueue queue = new ArrayBlockingQueue<>(5);
Runnable producer = () -> {
int value = 1;
try {
while (true) {
queue.put(value);
System.out.println("Produced: " + value);
value++;
Thread.sleep(500);
}
} catch (Exception e) {
e.printStackTrace();
}
};
Runnable consumer = () -> {
try {
while (true) {
int val = queue.take();
System.out.println("Consumed: " + val);
Thread.sleep(800);
}
} catch (Exception e) {
e.printStackTrace();
}
};
new Thread(producer).start();
new Thread(consumer).start();
}
}
5. Multiple Producers & Consumers
ExecutorService service = Executors.newFixedThreadPool(4); service.submit(producer); service.submit(producer); service.submit(consumer); service.submit(consumer);
6. Graceful Shutdown Strategy
Use a poison pill:
queue.put(-1); // special value indicating stop
Consumers stop when they read -1.
7. Real-World Use Cases
- Logging frameworks
- Thread pool work queues
- Messaging systems
- Data streaming pipelines
- Producer–Consumer microservices
Conclusion
BlockingQueue simplifies the producer–consumer problem by handling all synchronization internally. It is modern, cleaner, thread-safe, and widely used in enterprise applications.
Comments
Post a Comment