Зачем нужен Lock?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем нужен 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
-
Явный контроль — четко видно, где начинается и заканчивается критическая секция
-
tryLock() — попытка получить блокировку с таймаутом или без ожидания:
if (lock.tryLock(1, TimeUnit.SECONDS)) { try { // работаем с ресурсом } finally { lock.unlock(); } } else { // не смогли получить блокировку } -
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(); } } -
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 разделение или условные переменные.