Зачем явные Lock
synchronized прост и хорош по умолчанию, но негибок: нельзя попробовать взять замок без блокировки, прервать ожидание, задать честность или иметь несколько условий ожидания. Для этого есть java.util.concurrent.locks.Lock и его реализация ReentrantLock.
ReentrantLock даёт tryLock() (взять, если свободен), tryLock(timeout) (с таймаутом — мощное средство против deadlock), lockInterruptibly() (прерываемое ожидание), опциональную fairness (FIFO-порядок ценой пропускной способности) и несколько Condition.
Дисциплина unlock в finally
В отличие от synchronized, явный замок не освобождается автоматически. Его ОБЯЗАТЕЛЬНО снимают в finally: иначе исключение в критической секции оставит замок навсегда удержанным, и все ожидающие потоки зависнут.
lock() ставят перед try, а unlock() — в finally.
lock.lock();
try {
updateState();
} finally {
lock.unlock(); // всегда, даже при исключении
}
ReadWriteLock и StampedLock
ReentrantReadWriteLock разделяет доступ: много читателей одновременно ИЛИ один писатель. Выгоден для read-heavy данных, где чтения далеко преобладают; при частой записи выигрыша нет, а накладные расходы выше.
StampedLock (не реентерабельный!) добавляет оптимистичное чтение: tryOptimisticRead() возвращает «штамп», читаете данные, затем validate(stamp) проверяет, не было ли записи; если была — перечитываете под обычным read-локом. Это очень быстро для read-mostly, но требует аккуратности и не поддерживает reentrancy/Condition.
Condition
Condition — это аналог wait/notify для явных замков: lock.newCondition() даёт объект с await()/signal()/signalAll(). Главное преимущество — несколько разных условий на одном замке (например, notFull и notEmpty в ограниченной очереди), чего synchronized-монитор не умеет.
Правила те же: await() вызывают в цикле while по условию и под удерживаемым замком.
Condition notEmpty = lock.newCondition();
// потребитель:
lock.lock();
try { while (queue.isEmpty()) notEmpty.await(); take(); }
finally { lock.unlock(); }
Как отвечать на собеседовании
Назовите, что даёт ReentrantLock сверх synchronized (tryLock/timeout/interruptible/fairness/несколько Condition), подчеркните unlock в finally, объясните выигрыш ReadWriteLock на read-heavy и оптимистичное чтение StampedLock (не reentrant). Свяжите Condition с несколькими условиями на одном замке.