Gymterview
middle

В чем разница между notify() и notifyAll()?

Метод notify() пробуждает один произвольный поток из wait set монитора, а notifyAll() пробуждает все ожидающие потоки. Выбор пробуждаемого потока при notify() не определён спецификацией и зависит от реализации JVM.

Сравнение

Критерий notify() notifyAll()
Количество пробуждённых потоков Один (произвольный) Все
Какой именно поток Не определено (зависит от JVM) Все, но монитор захватит только один
Риск потери сигнала Да — может разбудить «не тот» поток Нет — все потоки проверят условие
Производительность Выше (меньше переключений контекста) Ниже при большом числе ожидающих

Проблема потери сигнала с notify()

Если несколько потоков ждут на одном мониторе, но с разными условиями, notify() может пробудить поток, для которого условие всё ещё не выполнено:

Пример потери сигнала
// Поток A ждёт: while (!dataReady) lock.wait();
// Поток B ждёт: while (!spaceAvailable) lock.wait();

// Производитель вызывает notify() после записи данных
// JVM может разбудить поток B, а не A
// Поток B проверит spaceAvailable — оно false — снова wait()
// Сигнал потерян, поток A продолжает спать

Когда безопасно использовать notify()

notify() безопасен только при одновременном выполнении двух условий:

  1. Все ожидающие потоки ждут одного и того же условия.
  2. Каждое уведомление актуально максимум для одного потока.

Во всех остальных случаях используйте notifyAll().

Вывод

На практике notifyAll() — более безопасный выбор. Используйте notify() только при уверенности, что условие ожидания одинаково для всех потоков в wait set. Для более гибкого управления используйте Condition из ReentrantLock, который позволяет создавать отдельные очереди ожидания для разных условий.

На собеседовании: объясните проблему потери сигнала (lost notification) и когда notify() безопасен. Это показывает глубокое понимание механизма.