Topic 5.8
Producer / Consumer
In one line
The fundamental async pattern: producers enqueue, consumers dequeue — decoupling, back-pressure, and rate mismatch handling.
Think of it like this
A dosa stall: the cook keeps making dosas and stacking them (producer), while customers keep taking dosas off the stack to eat (consumer). If dosas pile up faster than they're eaten, the stack grows; if customers outrun the cook, they wait.
Key ideas
- 01
BlockingQueue as the connector: put() back-pressures the producer; take() parks the consumer.
- 02
Multiple producers/consumers are supported by the queue itself.
- 03
Ordering is FIFO per queue unless partitioned (that's Kafka's job — Phase 10).
- 04
Shutdown discipline: poison pill or closed flag so consumers drain and exit, not hang.
- 05
Failure inside a consumer: catch, retry-inline bounded, else DLQ — never silently drop.
- 06
The pattern IS the design: notification fan-out, video transcoding pipeline, event processing.
Java / Spring map
- →
ArrayBlockingQueue / LinkedBlockingQueue + two ExecutorServices.
Code & diagrams
The canonical pattern with a poison pill for clean shutdown.
public class Pipeline {
private static final String POISON = "__STOP__";
private final BlockingQueue<String> q = new ArrayBlockingQueue<>(128);
public void start() {
ExecutorService producers = Executors.newFixedThreadPool(2);
ExecutorService consumers = Executors.newFixedThreadPool(4);
for (int i = 0; i < 2; i++) {
producers.submit(() -> {
for (int job = 0; job < 1_000; job++) {
try { q.put("job-" + job); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
q.offer(POISON, 5, TimeUnit.SECONDS); // one poison pill per producer
});
}
for (int i = 0; i < 4; i++) {
consumers.submit(() -> {
while (true) {
try {
String job = q.take(); // blocks → no busy-wait
if (POISON.equals(job)) break;
process(job);
} catch (InterruptedException e) { Thread.currentThread().interrupt(); break; }
}
});
}
}
private void process(String job) { /* simulate work */ }
}
// Production shape: the queue IS the back-pressure;
// if consumers are slower, producers block — nothing is ever lost.Explain without notes
Why a poison pill and not just a boolean flag? What happens to queued work on flag-stop?
Practice
Add a bounded retry around process() with a manual dead-letter list you can inspect.
Trade-offs
- ↔
In-memory queue dies with the JVM; Kafka (Phase 10) is the durable, replayable, partitioned upgrade.
Run it in production
You've designed it. Now build, operate, and break the same idea hands-on in the DevOps courses:
Completion checklist
I can build a producer/consumer pipeline and explain shutdown, back-pressure, and failure handling.