← Назад к вопросам

Как работают методы класса Object связанные с многопоточностью

2.7 Senior🔥 141 комментариев
#JVM и память#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Обзор методов класса Object для многопоточности

Класс Object в Java предоставляет несколько ключевых методов для управления потоками и синхронизации, которые являются фундаментальными для многопоточности. Эти методы — wait(), notify() и notifyAll() — работают в связке с механизмом мониторов и являются основой для реализации паттерна "wait-notify".

Механизм мониторов

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

public class SharedResource {
    private boolean condition = false;

    public synchronized void waitForCondition() throws InterruptedException {
        while (!condition) {
            // Поток освобождает монитор и ждет
            wait();
        }
        // Выполнить действие после удовлетворения условия
        System.out.println("Condition met!");
    }

    public synchronized void setCondition() {
        condition = true;
        // Поток, владеющий монитором, сигнализирует ожидающим потокам
        notifyAll();
    }
}

Подробное описание методов

Метод wait()

  • Освобождение монитора: Когда поток вызывает wait(), он освобождает монитор объекта, позволяя другим потокам войти в синхронизированный блок по этому же объекту.
  • Состояние ожидания: Поток переходит в состояние WAITING (или TIMED_WAITING при использовании wait(long timeout)). Он не будет выполняться до получения сигнала.
  • Проверка условия: Обычно используется в цикле (например, while(!condition) wait()), чтобы избежать проблем ложных пробуждений (spurious wakeups).

Метод notify()

  • Сигнал одному потоку: Выбирает один случайный поток из множества ожидающих на мониторе этого объекта и переводит его из состояния WAITING в состояние BLOCKED. Поток не начинает выполнение немедленно — он должен сначала перезахватить монитор.
  • Непредсказуемость выбора: Не гарантирует, какой именно поток будет пробужден, что может привести к неэффективному планированию.

Метод notifyAll()

  • Сигнал всем потокам: Пробуждает все потоки, ожидающие на мониторе данного объекта. Все они переходят в состояние BLOCKED и будут пытаться перезахватить монитор после того, как текущий поток его освободит (выходя из синхронизированного блока).
  • Поведение при захвате монитора: Когда монитор освобождается, пробужденные потоки борются за его захват. Только один сможет захватить его и продолжить выполнение; остальные вернутся в состояние BLOCKED, ожидая следующей возможности.

Ключевые особенности и правила использования

  • Только в synchronized: Эти методы должны вызываться только из синхронизированного контекста, иначе IllegalMonitorStateException.
  • Состояние потока: Поток, вызвавший wait(), находится в состоянии WAITING, а пробужденные потоки — в BLOCKED.
  • Перезахват монитора: Пробужденный поток не продолжает выполнение сразу — он должен успешно перезахватить монитор объекта.
  • Отличие от Thread.sleep(): sleep() не освобождает монитор, а wait() освобождает и требует перезахвата.
  • Проблема ложных пробуждений: Явление, когда поток может быть пробужден без явного вызова notify(). Решается путем проверки условия в цикле.

Пример практического использования

Рассмотрим классическую задачу "Producer-Consumer" с использованием этих методов.

public class MessageQueue {
    private final Queue<String> queue = new LinkedList<>();
    private final int maxSize;

    public MessageQueue(int maxSize) {
        this.maxSize = maxSize;
    }

    // Метод производителя
    public synchronized void produce(String message) throws InterruptedException {
        while (queue.size() == maxSize) {
            wait(); // Ждет, если очередь заполнена
        }
        queue.add(message);
        notifyAll(); // Сигнализирует потребителям и другим производителям
    }

    // Метод потребителя
    public synchronized String consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait(); // Ждет, если очередь пуста
        }
        String message = queue.poll();
        notifyAll(); // Сигнализирует производителям и другим потребителям
        return message;
    }
}

Современные альтернативы

Хотя wait()/notify() остаются важными для понимания основ, в современных приложениях на Android чаще используются более высокоуровневые инструменты:

  • java.util.concurrent пакет: Lock, Condition, BlockingQueue, Semaphore.
  • Корутины в Kotlin: suspend функции, Channel, Flow, Mutex.
  • ExecutorService и Futures: Для управления пулами потоков и асинхронными задачами.

Итог

Методы wait(), notify() и notifyAll() класса Object обеспечивают базовый механизм кооперативной синхронизации потоков через мониторы объектов. Их правильное использование требует строгого соблюдения условия — вызов только из синхронизированного блока, и понимания переходов состояний потока (WAITING -> BLOCKED -> RUNNING). Они являются исторической основой многопоточности в Java, но в реальной разработке на Android, особенно с Kotlin, часто заменяются более безопасными и выразительными современными API.