← Назад к вопросам

Зачем нужен Lock?

2.0 Middle🔥 231 комментариев
#SOLID и паттерны проектирования#Многопоточность#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Зачем нужен Lock?

Lock (интерфейс из пакета java.util.concurrent.locks) — это инструмент для управления синхронизацией доступа к ресурсам в многопоточном окружении. Он предоставляет более гибкую альтернативу встроенным синхронизированным блокам (synchronized).

Основная проблема, которую решает Lock

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

  • Race conditions — ситуациям, когда результат зависит от того, в каком порядке потоки выполнят код
  • Data corruption — повреждению данных при неправильном доступе
  • Inconsistent state — несогласованному состоянию объекта
// Проблема без синхронизации
private int counter = 0;

public void increment() {
    counter++;  // НЕ потокобезопасно! Это 3 операции:
                // 1. Прочитать значение
                // 2. Прибавить 1
                // 3. Записать результат
}

Решение с Lock

Lock обеспечивает взаимное исключение — только один поток может держать блокировку в одный момент времени.

private final Lock lock = new ReentrantLock();
private int counter = 0;

public void increment() {
    lock.lock();
    try {
        counter++;  // Теперь это безопасно
    } finally {
        lock.unlock();  // ВАЖНО: всегда освобождаем в finally
    }
}

Преимущества Lock над synchronized

  1. Явный контроль — четко видно, где начинается и заканчивается критическая секция

  2. tryLock() — попытка получить блокировку с таймаутом или без ожидания:

    if (lock.tryLock(1, TimeUnit.SECONDS)) {
        try {
            // работаем с ресурсом
        } finally {
            lock.unlock();
        }
    } else {
        // не смогли получить блокировку
    }
    
  3. ReadWriteLock — разные блокировки для чтения и записи:

    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    
    public void read() {
        rwLock.readLock().lock();
        try {
            // Несколько потоков могут одновременно читать
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    public void write() {
        rwLock.writeLock().lock();
        try {
            // Только один поток пишет, остальные ждут
        } finally {
            rwLock.writeLock().unlock();
        }
    }
    
  4. Fairness — справедливое распределение блокировки между потоками:

    private final Lock lock = new ReentrantLock(true);  // fair lock
    

ReentrantLock в деталях

Most common implementation is ReentrantLock:

  • Reentrant — один поток может получить одну и ту же блокировку несколько раз (счетчик увеличивается)
  • Важно вызвать unlock() столько же раз
private final Lock lock = new ReentrantLock();

public void methodA() {
    lock.lock();
    try {
        methodB();  // Тот же поток получает lock еще раз — OK
    } finally {
        lock.unlock();
    }
}

public void methodB() {
    lock.lock();
    try {
        // код
    } finally {
        lock.unlock();
    }
}

Когда использовать Lock?

  • Когда нужен таймаут при попытке получить блокировку
  • Когда нужна ReadWriteLock для оптимизации чтения
  • Когда нужна справедливость в распределении блокировки
  • Когда нужна Condition для ожидания специфических условий:
    private final Lock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    
    public void wait_for_element() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await();  // Ждем, пока очередь не заполнится
            }
        } finally {
            lock.unlock();
        }
    }
    

Итоги

Lock — это мощный инструмент для управления доступом к общим ресурсам в многопоточной среде. Он решает проблемы race conditions и обеспечивает потокобезопасность. Lock предпочтительнее synchronized когда нужна гибкость: таймауты, fair scheduling, read-write разделение или условные переменные.

Зачем нужен Lock? | PrepBro