Ответ
Монитором является сам объект, у которого вызываются методы 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'е ждал! Ёпта!
}
Вот и вся магия. Главное — синхронизироваться по одному и тому же объекту, иначе получится как в том анекдоте: один кричит "Муму!", а другой его в озеро кидает. Полный пиздец и непонимание.