BlockingQueue producer-consumer

Multithreading · lesson 12 of 38 · 3 min read

Wire producers to consumers with a bounded queue that does all the waiting for you.

Open this lesson in the learning hub

Key points

  • BlockingQueue is the producer-consumer pattern already written and tested. No hand-rolled wait/notify needed.
  • put() blocks while the queue is full, take() blocks while it is empty. That blocking is your back-pressure.
  • Always bound it. ArrayBlockingQueue or a sized LinkedBlockingQueue. An unbounded queue turns a slow consumer into an OutOfMemoryError.
  • When dropping work beats waiting, use offer(timeout) and poll(timeout) instead.
  • Signal the end with a poison pill, one per consumer, so every consumer gets its own exit ticket.

Example

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Main {
    static final String DONE = "__done__";

    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(4);   // bounded = back-pressure

        Thread producer = new Thread(() -> {
            try {
                for (int i = 1; i <= 8; i++) queue.put("item-" + i); // blocks while full
                queue.put(DONE);                                     // poison pill
            } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        });

        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    String item = queue.take();                      // blocks while empty
                    if (DONE.equals(item)) break;
                    System.out.println("consumed " + item);
                    Thread.sleep(20);                                // slow consumer on purpose
                }
            } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        });

        producer.start(); consumer.start();
        producer.join();  consumer.join();
        System.out.println("queue empty: " + queue.isEmpty());
    }
}

A bounded queue is back-pressure you get for free.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Multithreading course, and every lesson in it is listed on the Multithreading contents page.