BlockingQueue

A `BlockingQueue` is a thread-safe queue designed to coordinate producers and consumers. Its `put` and `take` operations wait automatically when the queue is full or empty, respectively.

`BlockingQueue` is part of Java's concurrent collections and provides thread-safe access for multiple threads. It is commonly used when one or more producer threads generate work and consumer threads process it.

The key behavior is blocking: `put(e)` waits while a bounded queue has no remaining capacity, and `take()` waits while the queue is empty. This lets threads coordinate without manually writing `wait`/`notify` logic for the queue itself.

There are also non-blocking and timed operations. `offer(e)` returns immediately with `false` if an item cannot be inserted, while `poll()` returns `null` when no item is available. Timed variants such as `offer(e, timeout, unit)` and `poll(timeout, unit)` wait only for a limited period.

Common implementations include `ArrayBlockingQueue`, which is bounded, `LinkedBlockingQueue`, which can be bounded by a supplied capacity, and `PriorityBlockingQueue`, which orders elements by priority rather than insertion order. The appropriate implementation depends on whether capacity limits and ordering requirements matter.

Key points

Interview questions

What is the difference between `put`, `offer`, `take`, and `poll`?

`put` inserts an element and waits if a bounded queue has no available capacity. `offer` attempts insertion immediately and returns whether it succeeded, with a timed overload available. `take` removes an element and waits if the queue is empty. `poll` returns immediately with `null` when there is no element, with a timed overload available.

What problem does `BlockingQueue` solve?

It provides a thread-safe way for producers and consumers to exchange work. A consumer can wait for work with `take()` instead of repeatedly checking whether the queue is empty. A producer can wait for available capacity with `put()` when using a bounded queue. This reduces the need for custom synchronization around the shared queue.

Why is a bounded `BlockingQueue` useful in a backend service?

A bounded queue limits how much work can accumulate in memory. If producers are faster than consumers, `put()` eventually blocks instead of allowing the backlog to grow without a limit. This can provide backpressure and help protect downstream resources. The capacity should be chosen according to workload and resource constraints rather than arbitrarily.

Which `BlockingQueue` implementation would you choose for FIFO work processing?

`ArrayBlockingQueue` is a straightforward choice when a fixed capacity and FIFO ordering are required. `LinkedBlockingQueue` is another FIFO option and allows its capacity to be specified. If priority ordering is required instead, `PriorityBlockingQueue` may be more appropriate. The choice should follow the required capacity, ordering, and workload characteristics.

How should interruption be handled while calling `take()` or `put()`?

Both operations can throw `InterruptedException` while the thread is waiting. A method can propagate the exception when its API allows that, or it can catch it and restore the thread's interrupted status with `Thread.currentThread().interrupt()`. Swallowing the interruption without handling it can prevent higher-level cancellation logic from working correctly. The appropriate policy depends on the application's shutdown and cancellation design.