Почему этот producer/consumer с busy-loop под synchronized встаёт?
Поток-оператор и поток-продюсер делят phone. Каждый крутит бесконечный цикл внутри synchronized(phone). Программа встаёт — работает лишь один поток. Объясните почему и исправьте.
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() }
}
}
Найдите и исправьте ошибку.
Тот поток, что первым вошёл в synchronized(phone), держит монитор весь свой бесконечный цикл и никогда не отпускает его, поэтому другой вечно блокируется — фактически deadlock. Решение — wait/notify: поток зовёт phone.wait(), чтобы отпустить монитор в простое, а другой зовёт notify() после изменения состояния.
- ✗Думать, что
synchronizedотпускает монитор между итерациями цикла - ✗Винить гонку данных вместо никогда не отпускаемого монитора
- ✗Считать, что один
@Volatileпозволит двум busy-loop'ам кооперироваться
- →Почему
waitиnotifyдолжны вызываться при удержании монитора объекта? - →Как
waitотпускает монитор, а busywhile-цикл — нет?
Баг
synchronized(phone) захватывает монитор объекта phone на всё время выполнения блока. Внутри стоит while (true), поэтому поток, вошедший первым, никогда не выходит из блока и никогда не отпускает монитор. Второй поток навсегда блокируется на входе в свой synchronized(phone) — фактический deadlock (точнее, голодание второго потока).
override fun run() = synchronized(phone) {
while (true) { /* ... */ } // ❌ монитор удерживается вечно
}
Исправление
Используйте wait/notify. wait() атомарно отпускает монитор и засыпает, давая другому потоку войти; notify() будит ожидающего после изменения состояния.
// оператор
synchronized(phone) {
while (true) {
while (phone.call == null) phone.wait() // ✅ отпускает монитор
handle(phone.call!!); phone.call = null
phone.notify()
}
}
// продюсер
synchronized(phone) {
while (true) {
while (phone.call != null) phone.wait()
phone.call = IncomingCall()
phone.notify()
}
}