Sobes.tech
Назад к вопросам
Junior — Middle
54

Применяются ли в практике такие механизмы, как synchronized, wait и notifyAll, для управления потоками?

Ответ от нейросети

sobes.tech AI

Что хотят услышать интервьюеры:

Да, эти механизмы в практике применяются, но в основном как базовые низкоуровневые средства синхронизации. synchronized используют для защиты критических секций и обеспечения взаимного исключения, а wait/notifyAll — для координации потоков через условную синхронизацию. На практике их часто заменяют более высокоуровневыми инструментами из java.util.concurrent, но понимать их необходимо.

Определение:

synchronized обеспечивает, что в один момент времени код выполняет только один поток для конкретного монитора объекта или класса.
wait() переводит поток в ожидание и освобождает монитор, пока другой поток не вызовет уведомление.
notifyAll() пробуждает все потоки, ожидающие на том же мониторе; после этого они соревнуются за повторный захват монитора.

Пример использования:

Классический пример — ограниченная очередь для producer-consumer.

import java.util.LinkedList;
import java.util.Queue;

class BoundedBuffer<T> {
    private final Queue<T> queue = new LinkedList<>();
    private final int capacity;

    public BoundedBuffer(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T item) throws InterruptedException {
        while (queue.size() == capacity) {
            wait();
        }
        queue.add(item);
        notifyAll();
    }

    public synchronized T take() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }
        T item = queue.poll();
        notifyAll();
        return item;
    }
}

Пояснение кода:

В этом примере synchronized на методах put и take защищает доступ к очереди: одновременно работает только один поток.
Если очередь заполнена, производитель вызывает wait() и засыпает, освобождая монитор, чтобы потребитель мог забрать элемент.
Если очередь пуста, потребитель тоже ждёт.
После изменения состояния очереди вызывается notifyAll(), чтобы разбудить все ожидающие потоки; они заново проверяют условие в while, потому что пробуждение не гарантирует, что условие уже стало истинным.

Ключевые моменты:

  • synchronized — это защита критической секции и видимость изменений между потоками.
  • wait() и notifyAll() можно вызывать только владея монитором объекта, то есть внутри synchronized.
  • Условие ожидания нужно проверять в while, а не в if, из-за ложных пробуждений и гонок.
  • notifyAll() часто безопаснее notify(), особенно если у объекта может быть несколько типов ожидающих потоков.
  • В реальной практике чаще используют Lock, Condition, BlockingQueue, Semaphore, потому что они удобнее и выразительнее.
  • Эти механизмы полезны для понимания низкоуровневой модели синхронизации Java и для разбора legacy-кода.