Java Concurrency LLD: Build a Custom BlockingQueue From Scratch
At Apple and Amazon, implementing a thread-safe BlockingQueue is one of the most frequent concurrency challenges in Low-Level Design (LLD) interviews. It immediately exposes whether you understand thread coordination, thread-safe state mutation, and JVM internals, or merely rely on off-the-shelf utilities.
The Mistake Most Candidates Make
- Relying on intrinsic locks (
synchronized) withnotifyAll(), which triggers the "thundering herd" problem by waking up both producers and consumers simultaneously. - Checking capacity bounds using an
ifstatement instead of awhileloop, leaving the queue vulnerable to state corruption via spurious wakeups. - Failing to structure lock acquisition with a proper
try-finallyblock, which causes unrecoverable deadlocks if a thread encounters an unexpected exception or interruption.
The Right Approach
- Core mental model: Decouple producer and consumer wait states using an explicit lock tied to two independent condition queues.
-
Key entities/classes:
ReentrantLock,Condition(notFull,notEmpty), array-based circular buffer (Object[]). -
Why it beats the naive approach: Direct signaling via
signal()wakes only the specific thread capable of making progress (producers wake consumers and vice versa), drastically reducing context switching.
Want to go deeper? javalld.com — machine coding interview problems with working Java code and full execution traces.
The Key Insight (Code)
public void put(E item) throws InterruptedException {
lock.lockInterruptibly();
try {
while (count == items.length) {
notFull.await();
}
enqueue(item);
notEmpty.signal();
} finally {
lock.unlock();
}
}
Key Takeaways
-
Dual Conditions, Single Lock: Bind two separate
Conditioninstances (notFull,notEmpty) to oneReentrantLockto isolate wait-sets for producers and consumers. -
Always Guard with
while: Always check wait conditions inside awhileloop to re-verify state invariants after waking up fromawait(). -
Symmetric Cross-Signaling: Modifying the queue must notify the opposite condition—producers signal
notEmptyafter adding an element, and consumers signalnotFullafter removing one.
Full working implementation with execution trace available at https://javalld.com/learn/blocking-queue
Top comments (0)