Why does this producer/consumer with a synchronized busy-loop stall?
An operator thread and a producer thread share a phone. Each runs an infinite loop inside synchronized(phone). The program stalls — only one thread ever runs. Explain why and fix it.
class Operator(val phone: Phone) : Thread() {
override fun run() = synchronized(phone) {
while (true) { phone.call?.let { handle(it); phone.call = null } }
}
}
class CallProducer(val phone: Phone) : Thread() {
override fun run() = synchronized(phone) {
while (true) { if (phone.call == null) phone.call = IncomingCall() }
}
}
Find and fix the bug.
Whichever thread enters synchronized(phone) first holds the monitor for its whole infinite loop and never releases it, so the other blocks forever — a deadlock. Fix it with wait/notify: a thread calls phone.wait() to release the monitor while idle, the other calls notify() after changing state.
- ✗Thinking
synchronizedreleases the monitor between loop iterations - ✗Blaming a data race instead of the never-released monitor
- ✗Believing
@Volatilealone lets the two busy-loops cooperate
- →Why must
waitandnotifybe called while holding the object's monitor? - →How does
waitrelease the monitor while a busywhileloop does not?
The bug
synchronized(phone) holds phone's monitor for the entire duration of the block. Inside is a while (true), so the thread that enters first never leaves the block and never releases the monitor. The second thread blocks forever trying to enter its own synchronized(phone) — an effective deadlock (more precisely, starvation of the second thread).
override fun run() = synchronized(phone) {
while (true) { /* ... */ } // ❌ monitor held forever
}
The fix
Use wait/notify. wait() atomically releases the monitor and sleeps, letting the other thread enter; notify() wakes the waiter after changing state.
// operator
synchronized(phone) {
while (true) {
while (phone.call == null) phone.wait() // ✅ releases the monitor
handle(phone.call!!); phone.call = null
phone.notify()
}
}
// producer
synchronized(phone) {
while (true) {
while (phone.call != null) phone.wait()
phone.call = IncomingCall()
phone.notify()
}
}