Ответ
В Java существует несколько примитивов синхронизации для управления многопоточностью, каждый решает конкретную задачу.
Основные блокировки и координация:
-
synchronized— ключевое слово для создания мониторных блокировок методов или блоков кода.synchronized (lockObject) { // Критическая секция }Почему: Гарантирует атомарность и видимость изменений в рамках одного монитора.
-
volatile— модификатор переменной, обеспечивающий видимость её изменений для всех потоков.private volatile boolean running;Почему: Запрещает кэширование переменной в регистрах потока, чтение всегда происходит из основной памяти.
-
wait(),notify(),notifyAll()— методы классаObjectдля условной блокировки и пробуждения потоков.synchronized (obj) { while (!condition) { obj.wait(); // Освобождает монитор 'obj' и ждёт } // Действие obj.notifyAll(); // Пробуждает все ожидающие потоки }Почему: Позволяют потокам эффективно ждать выполнения определённого условия, освобождая блокировку.
Гибкие примитивы из java.util.concurrent:
-
ReentrantLock— явная, повторно входимая блокировка с расширенными возможностями (честность, опрос, прерывание).Lock lock = new ReentrantLock(); lock.lock(); try { // Критическая секция } finally { lock.unlock(); // Важно освобождать в finally-блоке }Почему: Предоставляет больше контроля, чем
synchronized(например,tryLock(),lockInterruptibly()). -
Semaphore— ограничивает количество потоков, одновременно получающих доступ к ресурсу.Semaphore sem = new Semaphore(5); // Максимум 5 потоков sem.acquire(); // Уменьшает счётчик (ждёт, если он 0) try { // Работа с ограниченным ресурсом } finally { sem.release(); // Увеличивает счётчик }Почему: Полезен для управления доступом к пулу ресурсов (например, подключениям к БД).
-
CountDownLatch— одноразовый барьер, который заставляет потоки ждать завершения набора операций.CountDownLatch latch = new CountDownLatch(3); // В рабочих потоках: doWork(); latch.countDown(); // Уменьшает счётчик // В главном потоке: latch.await(); // Блокируется, пока счётчик не станет 0Почему: Идеален для ожидания инициализации или старта нескольких сервисов.
-
CyclicBarrier— многоразовый барьер, где потоки ждут друг друга в определённой точке.CyclicBarrier barrier = new CyclicBarrier(3); // В каждом из 3 потоков: doWorkPart(); barrier.await(); // Все ждут здесь, пока не соберутся все 3 continueWork();Почему: Полезен для параллельных алгоритмов, где этапы работы должны быть синхронизированы.
-
Phaser— более гибкий и многоразовый барьер с динамической регистрацией участников. Почему: Позволяет потокам присоединяться и покидать барьер между фазами, что удобно для сложных сценариев.
Выбор примитива зависит от задачи:
synchronized/ReentrantLock— для эксклюзивного доступа к данным.Semaphore— для ограничения параллелизма.CountDownLatch— для однократного ожидания событий.CyclicBarrier/Phaser— для синхронизации работы потоков по фазам.