"Implement a bounded blocking queue" is one of the most common concurrency interview questions. It's the producer-consumer problem in disguise: producers add items, consumers remove them, and the buffer has a fixed capacity.
put blocks while the buffer is full.take blocks while it is empty.class BoundedQueue:
def __init__(self, cap):
self.q, self.cap = deque(), cap
self.lock = Lock()
self.not_full = Condition(self.lock)
self.not_empty = Condition(self.lock)
def put(self, x):
with self.lock:
while len(self.q) == self.cap:
self.not_full.wait()
self.q.append(x)
self.not_empty.notify()
def take(self):
with self.lock:
while not self.q:
self.not_empty.wait()
x = self.q.popleft()
self.not_full.notify()
return x
while, not if, around wait(). Wake-ups can be spurious, and another consumer may grab the item between the notify and your thread re-acquiring the lock. Re-check the condition every time.notify_all, but wastes wake-ups; separate conditions are cleaner.Two counting semaphores (empty slots and filled slots) plus a mutex give the classic textbook version. In production you'd use the language's built-in queue (queue.Queue, Java's ArrayBlockingQueue, Go channels); saying so shows judgment, as long as you can also build it yourself.
Shutdown: add a closed flag and wake everyone with notify_all so blocked threads can exit. Timeouts: use wait(timeout) and recompute the remaining time in the loop. Throughput: a lock-free ring buffer works for single-producer single-consumer, but it's rarely needed in an interview.
Disclosure: I work with Linkjob.ai on AI interview tools; this walkthrough stands on its own.