Как работать с интерфейсом Lock в Java для синхронизации?

«Как работать с интерфейсом Lock в Java для синхронизации?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

java.util.concurrent.locks.Lock — это интерфейс для более гибкой и расширенной синхронизации потоков по сравнению с synchronized. Основная реализация — ReentrantLock.

Базовый шаблон использования: Всегда вызывайте unlock() в блоке finally, чтобы гарантировать освобождение блокировки даже при возникновении исключения.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

Lock lock = new ReentrantLock();
lock.lock(); // Захват блокировки

try {
    // Критическая секция (доступ к общему ресурсу)
    sharedResource.modify();
} finally {
    lock.unlock(); // Гарантированное освобождение
}

Ключевые возможности Lock (преимущества перед synchronized):

  • Попытка захвата с таймаутом (tryLock):

    if (lock.tryLock(1, TimeUnit.SECONDS)) { // Пытается захватить 1 секунду
        try { /* работа с ресурсом */ }
        finally { lock.unlock(); }
    } else {
        // Действие, если блокировка не получена
    }
  • Прерываемое ожидание (lockInterruptibly):

    lock.lockInterruptibly(); // Поток может быть прерван во время ожидания
    try { /* ... */ } finally { lock.unlock(); }
  • Честность (fairness):

    Lock fairLock = new ReentrantLock(true); // Очередь FIFO для ожидающих потоков
  • Условные переменные (Condition): Позволяют организовать точечное ожидание/сигнализацию.

    Condition condition = lock.newCondition();
    // В потоке 1:
    lock.lock();
    try {
        while (!conditionMet) {
            condition.await(); // Освобождает lock и ждёт
        }
    } finally { lock.unlock(); }
    
    // В потоке 2:
    lock.lock();
    try {
        conditionMet = true;
        condition.signal(); // Будит один ожидающий поток
    } finally { lock.unlock(); }

Когда использовать Lock? В сложных сценариях, где требуется контроль над процессом блокировки: таймауты, прерывания, чередование блокировок или использование нескольких условий ожидания.