Что такое монитор в контексте многопоточности Java?

Ответ

Монитор — это механизм синхронизации, встроенный в каждый объект Java, который обеспечивает взаимное исключение (mutual exclusion) для критических секций кода.

Как это работает:

  1. У каждого объекта (Object) есть связанный с ним монитор (или встроенная блокировка).
  2. Ключевое слово synchronized используется для захвата монитора объекта.
  3. Только один поток в один момент времени может владеть монитором конкретного объекта и выполнять код в synchronized блоке или методе для этого объекта.
  4. Остальные потоки, пытающиеся войти в синхронизированную секцию для того же объекта, блокируются и помещаются в entry set (очередь ожидания монитора).

Реализация:

// 1. Синхронизированный метод (монитором выступает экземпляр класса 'this')
public class SharedCounter {
    private int count = 0;

    public synchronized void increment() {
        count++; // Критическая секция защищена монитором 'this'
    }
}

// 2. Синхронизированный блок с явным указанием объекта-монитора
public class SharedResource {
    private final Object lock = new Object(); // Специальный объект для монитора
    private int data;

    public void updateData(int value) {
        // Несинхронизированная работа...
        synchronized (lock) { // Захват монитора объекта 'lock'
            data = value; // Критическая секция
        }
    }
}

Важные особенности монитора в Java:

  • Реентерабельность (Reentrancy): Поток, уже владеющий монитором, может повторно входить в синхронизированные блоки по тому же монитору (счётчик вхождений увеличивается). Это предотвращает самоблокировку (self deadlock).
  • wait(), notify(), notifyAll(): Методы класса Object, которые позволяют потокам координировать работу. Поток, владеющий монитором, может вызвать wait(), чтобы освободить монитор и перейти в wait set. Другой поток, вызвав notify()/notifyAll(), может разбудить ожидающие потоки.

Монитор — это базовая, встроенная в JVM реализация мьютекса с дополнительными возможностями для координации потоков.

Ответ 18+ 🔞

Давай я тебе на пальцах объясню, что за хуйня этот монитор в Java, а то в учебниках пишут так, что мозги набок выворачивает.

Представь себе сортир в общаге, один на весь этаж. Вот этот сортир — это и есть объект. А монитор — это та самая хреновина на двери, засов такой. Пока засов задвинут изнутри — дверь не открывается, все довольны.

Как эта ебля работает:

  1. У каждого объекта, даже у самого простого new Object(), с завода вшит этот самый засов (монитор).
  2. Ключевое слово synchronized — это как крикнуть «ЗАНЯТО!» и щёлкнуть этим засовом.
  3. Только один чувак может сидеть в сортире и делать свои дела. Остальные стоят в коридоре, чешут репу и ждут своей очереди (это и есть entry set).
  4. Как только первый выходит и засов щёлкает — следующий заскакивает.

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

// 1. Синхронизированный метод. Монитор — это сам объект (this), то есть дверь этого конкретного сортира.
public class SharedCounter {
    private int count = 0;

    public synchronized void increment() { // Крикнул "ЗАНЯТО!" на весь объект
        count++; // Справляй нужду, никто не помешает
    }
}

// 2. Синхронизированный блок. Тут мы можем выбрать, на какую конкретно дверь вешать засов.
public class SharedResource {
    private final Object lock = new Object(); // Отдельная кабинка, так сказать
    private int data;

    public void updateData(int value) {
        // Тут можно похавать бутерброд, посмотреть в окно — несинхронизированная хуйня
        synchronized (lock) { // А вот тут подошёл к своей кабинке 'lock' и щёлкнул засовом
            data = value; // Главное дело
        } // Выходишь, засов открывается
    }
}

А теперь про важные плюшки, которые многие забывают:

  • Реентерабельность (Reentrancy): Это когда ты уже в сортире, а тебе надо взять туалетную бумагу, которая тоже внутри сортира. Так вот, ты её спокойно берёшь, не выходя и не перезащёлкивая дверь. Поток, который уже владеет монитором, может заходить в другие synchronized блоки по тому же монитору. Счётчик просто увеличивается. Иначе бы мы все тут повесились на первом же рекурсивном вызове, ёпта.

  • wait(), notify(), notifyAll(): А это вообще магия. Допустим, ты зашёл в сортир, а там, блядь, бумаги нет. Так вот, wait() — это ты сел на унитаз и уснул, отпустив засов (монитор-то освободил!). Ты теперь в wait set (комнате ожидания бумаги). Другой поток (сантехник Вася) может подбросить рулон и крикнуть notify() — «Эй, там один, бумага есть!». Ты просыпаешься, но тебе ещё надо заново захватить монитор (дождаться, пока сортир свободен и щёлкнуть засовом), чтобы продолжить. notifyAll() — это Вася орёт на весь этаж «БУМАГА ПОДВЕЗЛИ!», просыпаются все, кто ждал.

Короче, монитор в Java — это не просто мьютекс, это такой мьютекс с довеском, который умеет не только не пускать, но и усыплять-будить потоки, когда условия не готовы. Всё гениальное — просто, как три кнопки на унитазе.