Phase 1: Requirements & Use Cases
Interviewer: "Design a blocking queue."
Candidate: "Let me confirm scope. We're building a bounded, thread-safe queue where producers block when the queue is full and consumers block when the queue is empty — the classic producer-consumer data structure. This is a single-process, in-memory design; no distributed messaging (no Kafka, no RabbitMQ). Does that match?"
Interviewer: "Yes. What use cases are you targeting?"
Candidate: "The core use cases are:
put(item)— insert an item; block if the queue is full until space is availabletake()— remove and return the head; block if the queue is empty until an item is availableoffer(item, timeout)— insert with a timeout; return false if capacity isn't available within the timeoutpoll(timeout)— retrieve with a timeout; return null if no item is available within the timeoutpeek()— inspect the head without removing it, non-blockingsize()/isEmpty()/isFull()— introspection"Interviewer: "Do you need to support multiple producers and multiple consumers concurrently?"
Candidate: "Yes — that's the core value of a blocking queue. Multiple producer threads should be able to call
put()and multiple consumer threads should be able to calltake()concurrently, with correct synchronization. Race conditions between producers competing for space or consumers competing for items must be handled."Interviewer: "What about unbounded queues?"
Candidate: "I'll design a bounded queue first since it's the interesting concurrency case — bounding forces the blocking behavior. I'll note where unbounded is a simple variant: set capacity to
Integer.MAX_VALUE…
In the full tutorial
- Phase 2: Object Modeling
- Phase 3: Class Design & Relationships
- Phase 4: Design Patterns Application
- Phase 5: Key Implementation
- Phase 6: Extensibility & Trade-offs