synchronized и locks
Тема дорожной карты · Java
synchronized в Java — это встроенное ключевое слово для блокировки с взаимным исключением, гарантирующее, что в критической секции в каждый момент времени находится не более одного потока, защищая общее изменяемое состояние от гонок данных и проблем видимости памяти на JVM. Ключевое слово synchronized может применяться к методам экземпляра (synchronized void update()), статическим методам (synchronized static void init()) или явным блокам (synchronized(lock) { ... }), где монитор (встроенная блокировка), связанный с указанным объектом или классом, захватывается перед входом и освобождается при выходе, включая аварийный выход через исключения. synchronized в Java предоставляет две гарантии: взаимное исключение (атомарность защищённого блока) и видимость памяти (изменения внутри блока synchronized видны всем последующим потокам, получающим тот же монитор) — те же гарантии, что volatile предоставляет только для видимости одной переменной. Для большинства современных требований Java Concurrency java.util.concurrent предлагает лучшие альтернативы — ReentrantLock для блокировки с таймаутом/прерыванием, ReadWriteLock для разделения читателей и писателей, StampedLock для оптимистичного чтения, и структуры данных без блокировок вроде ConcurrentHashMap и AtomicLong, — однако synchronized остаётся актуальным для простых критических секций с низкой конкуренцией и сигнализации через wait()/notify()/notifyAll(). Глубокое понимание synchronized в Java, включая риски взаимоблокировки, живой блокировки и конкуренции за блокировки при высоком числе потоков, является фундаментальным знанием для работы с Java Threads, Virtual Threads (Project Loom) и утилитами конкурентности java.util.concurrent.
Как это работает
synchronized и locks имеет много инструментов: Thread (низкоуровневый), Executor / ExecutorService (пулы потоков), CompletableFuture (async-композиция), Future, java.util.concurrent коллекции (ConcurrentHashMap, BlockingQueue, CopyOnWriteArrayList), атомарные примитивы (AtomicInteger, AtomicReference). Java 21 даёт virtual threads (Project Loom — миллионы лёгких потоков, идеально для I/O-bound). Синхронизация: synchronized, ReentrantLock, ReadWriteLock, StampedLock. Memory model (JMM) управляет видимостью.
Когда применять
ExecutorService вместо сырых Thread — пулы управляют жизненным циклом. CompletableFuture — для async-композиции (thenApply, thenCompose, allOf). На Java 21+ virtual threads делают блокирующий I/O практичным на высокой конкурентности (Executors.newVirtualThreadPerTaskExecutor()). ConcurrentHashMap — для shared-state. Прочтите "Java Concurrency in Practice" до серьёзного concurrent-кода.
Типичные ошибки
Ловушки synchronized и locks: shared mutable state без синхронизации (visibility-баги); volatile-"фикс" для race, требующих атомарности (volatile только видимость, не атомарный RMW); deadlock из-за двух локов в разном порядке; не daemon-threads когда нужно (JVM висит при shutdown); слишком много потоков для I/O-bound до Loom (новый ответ — virtual threads).