Explanation
What just happened
The consumer started first and parked in take(), because the queue was empty. The producer put a, which woke the consumer. The producer then filled the queue with b and c. With no room left, put(d) parked the producer. As soon as the consumer took b, a slot opened and d went in.
Try the what-if switch
unbounded: with new LinkedBlockingQueue<>(), put() never waits. The producer finishes at once and the jobs pile up in the queue. Nothing slows the producer down, so with a fast producer the queue grows until memory runs out.
The queue is the contract
Neither side knows about the other. They only share the queue:
put()waits when the queue is full: the producer slows down to the consumer’s speed.take()waits when it’s empty: the consumer rests until there is work.
That automatic slowing down is called backpressure.
Pick the right queue
ArrayBlockingQueue(n): fixed size, backpressure built in. A good default.LinkedBlockingQueue(): no limit unless you pass one. Without a limit,put()never waits.SynchronousQueue: no storage at all. Eachput()waits for atake(), a direct hand-off.
Stopping cleanly
A common pattern is a special “poison pill” item: the producer puts a marker like "STOP" last, and the consumer exits when it takes it.
You’ve already used this pattern: a ThreadPoolExecutor’s threads are consumers taking tasks from a BlockingQueue.
Common questions
How do put() and take() work on a BlockingQueue?
put() waits while the queue is full, and take() waits while it is empty. That keeps a fast producer at the consumer's speed, which is called backpressure.
ArrayBlockingQueue or LinkedBlockingQueue?
ArrayBlockingQueue(n) always has a limit, so put() can wait. new LinkedBlockingQueue<>() has no limit unless you pass one, so put() never waits and a fast producer fills memory.
How do I stop a consumer thread cleanly?
A common pattern is a poison pill: the producer puts a special marker last, and the consumer exits when it takes it.
Verified: this timeline matches a real program, checked in CI on JDK 21 and 25. How we verify. Unclear or wrong? Tell us