Чем монитор отличается от других примитивов синхронизации в Java?

Ответ

Монитор — это низкоуровневый, встроенный в каждый объект механизм, лежащий в основе ключевого слова synchronized. Другие примитивы синхронизации (из java.util.concurrent) — это высокоуровневые, более гибкие и производительные абстракции.

Сравнительная таблица:

Примитив Ключевая особенность Когда использовать
Монитор (synchronized) Встроен в язык, прост в использовании. Одна блокировка на объект (вход/выход). Простые случаи, когда достаточно базовой блокировки. Легаси-код.
ReentrantLock Явная блокировка. Расширенные возможности: tryLock(), честность, несколько Condition. Когда нужны возможности, отсутствующие у synchronized.
Semaphore Управляет доступом к пулу ресурсов (N разрешений). Не является блокировкой владения. Ограничение количества одновременных потоков, например, к соединению с БД.
CountDownLatch Ожидание завершения набора операций в других потоках. Счетчик уменьшается, но не сбрасывается. Запуск потоков одновременно или ожидание инициализации сервисов.
CyclicBarrier Ожидание, пока N потоков не достигнут барьера, затем все продолжают. Сбрасывается и используется повторно. Многоэтапные параллельные вычисления (например, итеративные алгоритмы).

Пример, где ReentrantLock предпочтительнее монитора:

import java.util.concurrent.locks.ReentrantLock;

public class InterruptibleLocking {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() throws InterruptedException {
        // Пытаемся захватить блокировку с возможностью прерывания ожидания
        if (lock.tryLock(1, TimeUnit.SECONDS)) {
            try {
                // Критическая секция
                System.out.println("Lock acquired by " + Thread.currentThread().getName());
            } finally {
                lock.unlock(); // Всегда освобождаем в finally
            }
        } else {
            System.out.println("Could not acquire lock");
        }
    }
}

Вывод: Монитор (synchronized) — это основа, но для сложных сценариев конкурентности следует использовать более специализированные классы из java.util.concurrent.*.

Ответ 18+ 🔞

А, ну ты смотри, какие у нас тут примитивы синхронизации разложили по полочкам, как в аптеке, блядь. Давай разберём эту хуйню, а то некоторые до сих пор думают, что synchronized — это венец эволюции и больше нихуя не надо.

Вот смотри, монитор — это как встроенный в каждый объект такой старый, добрый, тупой замок, ёпта. Надел synchronized — и всё, ты в домике, пока не выйдешь. Просто? Проще некуда. Но и гибкости, как у дохлой тряпки, блядь. Одна блокировка на объект, вход-выход, и всё, пиздец.

А потом пришли умные дядьки и наваяли java.util.concurrent. Это уже не просто замок, это целый арсенал, сука. Там тебе и отмычки, и таймеры, и возможность вежливо постучаться, а не ломиться в дверь с ноги.

Табличку эту читай, как мантру:

  • synchronized (монитор): Используй, когда задача проще пареной репы. Или когда ковыряешься в старом коде, который писал какой-то пидарас шерстяной лет десять назад. Работает — не трожь, блядь.
  • ReentrantLock: Вот это уже орудие, бля. Хочешь попробовать захватить блокировку на время (tryLock)? Хочешь прервать поток, который вечно ждёт? Хочешь честную очередь или несколько условий ожидания? Это всё сюда, ёбана. synchronized на такое способен, как кот на собаке — нихуя.
  • Semaphore: Это вообще не про владение, а про пропускную способность. Представь, что у тебя 5 кабинок в сортире, а народу — овердохуища. Semaphore как раз и следит, чтобы больше пяти ублюдков одновременно не засело. Идеально для ограничения подключений к чему-нибудь.
  • CountDownLatch: Штука, которая ждёт, пока все остальные сделают свою работу. Запустил 10 потоков-грузчиков и сидишь, куришь, пока эта защёлка не щёлкнет на ноль. Раз использовал — и выбросил, блядь. Не сбрасывается.
  • CyclicBarrier: А это уже циклическая хуйня. Собрались N потоков у барьера — все дружно пошли дальше. А потом снова собрались на следующем этапе. Для многоходовочных параллельных алгоритмов — то, что надо.

Вот тебе живой пример, почему synchronized иногда — мудя полная:

import java.util.concurrent.locks.ReentrantLock;

public class InterruptibleLocking {
    private final ReentrantLock lock = new ReentrantLock();

    public void performTask() throws InterruptedException {
        // Пытаемся захватить блокировку с возможностью прерывания ожидания
        if (lock.tryLock(1, TimeUnit.SECONDS)) {
            try {
                // Критическая секция
                System.out.println("Lock acquired by " + Thread.currentThread().getName());
            } finally {
                lock.unlock(); // Всегда освобождаем в finally
            }
        } else {
            System.out.println("Could not acquire lock");
        }
    }
}

Видишь? Мы говорим: «Дружище, дай мне блокировку, но если за секунду не справишься — да похуй, я пойду другие дела делать, а не висеть тут, как идиот». С synchronized ты бы просто повис в очереди на неопределёнку, пока тебя не вышибет InterruptedException, да и то не факт. А тут — всё цивильно, под контролем.

Так что вывод, блядь, простой: synchronized — это как молоток. Иногда им можно и гвоздь забить, и по лбу дать. Но если тебе нужно собрать хитроумную полку — бери нормальный инструментарий из java.util.concurrent.*, а не долбись монитором, как пещерный человек.