Что является монитором при вызове методов wait() и notify() в Java?

Ответ

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

Ключевые правила:

  • Поток должен владеть монитором объекта (находиться внутри synchronized-блока для этого объекта) перед вызовом wait(), notify() или notifyAll().
  • Вызов этих методов без захваченного монитора приводит к IllegalMonitorStateException.
  • Метод wait() временно освобождает монитор, позволяя другим потокам его захватить, и переводит текущий поток в состояние ожидания.

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

final Object lock = new Object();

synchronized (lock) { // Захват монитора объекта 'lock'
    while (!conditionMet) {
        lock.wait(); // Освобождает монитор и ожидает
    }
    // После пробуждения поток автоматически перезахватывает монитор
    // и может выполнять работу под защитой синхронизации
}

Метод notify() или notifyAll(), вызванный другим потоком из synchronized-блока по тому же объекту, будит один или все ожидающие потоки соответственно.

Ответ 18+ 🔞

А, ну это же классика, про которую все вроде слышали, а на деле половина нихуя не понимает, как оно работает! Смотри, монитор — это, по сути, сам объект, обычный такой объект, который ты создаёшь через new. Да-да, вот эта вот твоя new String("хлеб") — она уже с монитором в комплекте, как будто встроенный замок!

И вот этот самый монитор — он как раз и нужен, чтобы крутить эти ваши wait(), notify() и notifyAll(). Без него — пиздец и IllegalMonitorStateException тебе прямо в консоль.

Главные правила, которые надо вбить себе в башку, чтобы не выглядеть мудаком:

  • Чтобы позвать wait() или notify(), ты должен уже держать в лапах монитор этого объекта. То есть сидеть внутри synchronized (по_этому_самому_объекту) { ... }. Иначе — что ты будишь, чё ты ждёшь? Ты снаружи стоишь, как лох!
  • wait() — это не просто "поспать". Это такая хитрая операция: он отпускает монитор (чтобы другие потоки могли зайти и что-то сделать, пока этот спит), а сам поток — бац, и в ожидание. Умная жопа, короче.
  • Когда поток просыпается (после notify()), он не сразу бежит дальше. Сначала он как честный мужик должен заново захватить тот самый монитор, который отпустил. И только потом уже код после wait() выполняется. Всё честно, без гонок.

Вот смотри, как это в жизни выглядит, на примере:

final Object lock = new Object(); // Вот он, наш "замок" с монитором!

synchronized (lock) { // Захватываем монитор! Теперь мы в домике.
    while (!conditionMet) { // Проверяем условие. Нет условия? Ждём!
        lock.wait(); // Ключевой момент! Отпускаем монитор и засыпаем.
    }
    // Ура, нас разбудили и conditionMet == true!
    // Но прежде чем выполнять этот код, мы АВТОМАТИЧЕСКИ перехватываем монитор обратно.
    // Теперь можно спокойно работать, никто не помешает.
}

А где-то в другом месте, в другом потоке, какой-то добрый самаритянин делает так:

synchronized (lock) { // Захватывает ТОТ ЖЕ САМЫЙ монитор!
    conditionMet = true; // Меняет условие...
    lock.notifyAll(); // И будит всех, кто на этом lock'е ждал! Ёпта!
}

Вот и вся магия. Главное — синхронизироваться по одному и тому же объекту, иначе получится как в том анекдоте: один кричит "Муму!", а другой его в озеро кидает. Полный пиздец и непонимание.