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
- Thread-safe queue designed for concurrent producers and consumers.
- `put()` blocks when a bounded queue is full.
- `take()` blocks when the queue is empty.
- `offer()` and `poll()` provide non-blocking alternatives.
- Timed methods can wait for a specified duration.
- Implementations differ in capacity and ordering behavior.
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.