BlockingQueue Family — Java Interview Guide | Cracked Java
Senior

BlockingQueue Family

The producer-consumer toolbox. Which queue each Executors factory uses, why newFixedThreadPool can take down production.

Prereqs: queue-deque-priorityqueue

A BlockingQueue<E> is a thread-safe queue that adds blocking insert and remove operations: producers block when the queue is full, consumers block when it's empty. It's the backbone of producer-consumer pipelines and the work queue for every ThreadPoolExecutor.

The contract

public interface BlockingQueue<E> extends Queue<E> {
    void put(E e) throws InterruptedException;   // blocks if full
    E take() throws InterruptedException;        // blocks if empty
    boolean offer(E e, long t, TimeUnit u);      // bounded wait insert
    E poll(long t, TimeUnit u);                  // bounded wait remove
    boolean offer(E e);                          // non-blocking insert
    E poll();                                    // non-blocking remove
    void add(E e);                               // throws on full
    E remove();                                  // throws on empty
}

Three behaviors × insert/remove = the classic 4×2 matrix in the Javadoc.

The implementations at a glance

ImplementationBounded?OrderingBacked byNotable use
ArrayBlockingQueueyes (fixed)FIFOarray + 1 ReentrantLockclassic bounded pipeline
LinkedBlockingQueueoptionalFIFOlinked list + 2 lockshigh-throughput pipelines
SynchronousQueue0 capacitydirect handoffTransferStack / TransferQueuenewCachedThreadPool
PriorityBlockingQueueunboundedby Comparatorbinary heappriority work scheduling
DelayQueueunboundedby delay expiryheap of Delayedscheduled task triggers
LinkedTransferQueueunboundedFIFOlock-free (CAS)low-latency handoff
LinkedBlockingDequeoptionalFIFO/LIFOdoubly-linked listwork stealing

Why blocking?

Without blocking semantics, a producer would have to either:

  • Spin-loop on offer() — wasting CPU, or
  • Use wait()/notify() manually — error-prone, easy to deadlock.

put/take encapsulate this with Condition variables internally — clean, interruptible, fair-or-not.

Interruption is built-in

All blocking methods throw InterruptedException. A producer waiting on put can be cancelled by Thread.interrupt(). This is what makes BlockingQueue work with ExecutorService.shutdownNow().

The two laws of capacity

  1. Always bound your queue in production. Unbounded queues backed by LinkedBlockingQueue are the #1 cause of OOM in Java services — work piles up faster than it drains.
  2. Bound it small enough that backpressure propagates. A bounded queue lets producers feel the pressure and slow down (or reject). That's the whole point.

Questions

5 in this topic