advancedAdvanced & Design

How do you implement a producer-consumer setup with multiple producers and multiple consumers?

The core building block is a BlockingQueue, such as ArrayBlockingQueue or LinkedBlockingQueue. Multiple producer threads simply call put(), which blocks them automatically once the queue is full, giving natural backpressure without any extra coordination code, and multiple consumer threads call take(), which blocks them automatically once the queue is empty. All of the internal synchronization needed to make this safe with many producers and many consumers is handled entirely inside the BlockingQueue implementation. A clean shutdown is typically done by having a producer enqueue one poison pill per consumer thread once all real work has been submitted, and each consumer exits its loop as soon as it dequeues its poison pill. For monitoring, tracking the queue's current size, and a separate dropped-item counter if offer() is used instead of the blocking put(), gives good visibility into whether the pipeline is keeping up. With virtual threads available since Java 21, it's now practical to dedicate one lightweight virtual thread per producer and per consumer and run them on an effectively unbounded virtual thread executor.

Ready to master this question?

Generate a complete walkthrough — background, the full answer in plain language, a working code example explained line by line, a real-world scenario, common mistakes, and how this same question gets asked in different ways.

Sign in to generate a response

Next Step

Continue to What are the dangers of calling alien methods while holding a lock?← Back to all Java Concurrency & Multithreading questions