Что делает метод notify() в Java?

«Что делает метод notify() в Java?» — вопрос из категории Java Core, который задают на 22% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

notify() — это метод конечного (final) класса Object в Java, который используется для межпоточной синхронизации. Он пробуждает один произвольный поток, который находится в состоянии ожидания на том же самом мониторе (объекте), на котором был вызван notify().

Ключевые принципы:

  • Работает только внутри синхронизированного блока: Метод notify() должен вызываться из блока кода, защищенного synchronized на том же объекте-мониторе, на котором потоки вызвали wait().
  • Не освобождает монитор: Вызов notify() не приводит к немедленному освобождению монитора. Поток, вызвавший notify(), продолжает удерживать монитор до тех пор, пока не выйдет из своего синхронизированного блока (или метода).
  • Пробуждает один поток: Какой именно поток будет разбужен — зависит от реализации JVM, порядок не гарантируется.
  • Без эффекта, если нет ожидающих: Если на момент вызова notify() ни один поток не ожидает на этом мониторе (не вызвал wait()), то вызов игнорируется.

Пример (шаблон Producer-Consumer):

public class SharedResource {
    private int value;
    private boolean hasValue = false;
    private final Object lock = new Object();

    public void produce(int newValue) throws InterruptedException {
        synchronized (lock) {
            while (hasValue) { // Ожидание, если значение еще не потреблено
                lock.wait();
            }
            value = newValue;
            hasValue = true;
            System.out.println("Produced: " + value);
            lock.notify(); // Пробуждаем потребителя
        }
    }

    public void consume() throws InterruptedException {
        synchronized (lock) {
            while (!hasValue) { // Ожидание, если значения нет
                lock.wait();
            }
            System.out.println("Consumed: " + value);
            hasValue = false;
            lock.notify(); // Пробуждаем производителя
        }
    }
}

notify() vs notifyAll():

  • notify() — пробуждает один случайный поток из очереди ожидания. Эффективнее, но может привести к "голоданию" (starvation), если логика пробуждения неверна.
  • notifyAll() — пробуждает все потоки, ожидающие на этом мониторе. Потоки по очереди повторно захватывают монитор и проверяют условие в цикле while. Более безопасный, но менее производительный вариант.

Важно: Использование wait()/notify() напрямую считается низкоуровневым и подверженным ошибкам. В современной разработке предпочтительнее использовать высокоуровневые конструкции из java.util.concurrent (например, BlockingQueue, Condition).