ReentrantLock.lock(): примеры (JAVA)
ReentrantLock.lock(): voidОписание ReentrantLock.lock()
Метод ReentrantLock.lock() класса java.util.concurrent.locks.ReentrantLock устанавливает взаимоисключающую блокировку для текущего потока. Если блокировка свободна, поток получает её и продолжает выполнение. Если блокировка удерживается другим потоком, текущий поток блокируется до освобождения. Класс является реентрантным: поток, уже владеющий блокировкой, может выполнить lock() повторно без блокировки самого себя.
Основные варианты работы блокировок в API Lock/ReentrantLock:
void lock()- блокирующий вызов; не принимает аргументов и не возвращает значения. Не реагирует на прерывания во время ожидания.void lockInterruptibly() throws InterruptedException- блокирующий вызов, но реагирует на прерывание: при прерывании бросаетInterruptedException.boolean tryLock()- неблокирующий: пытается получить блокировку и сразу возвращаетtrue, если удалось, илиfalse, если нет.boolean tryLock(long time, TimeUnit unit) throws InterruptedException- пытается получить блокировку в течение указанного времени; возвращаетtrue, если удалось, иначеfalse; реагирует на прерывания.void unlock()- освобождает блокировку; при вызове потоком, который не владеет блокировкой, бросаетIllegalMonitorStateException. Возвращаемого значения нет.
Конструкторы ReentrantLock() и ReentrantLock(boolean fair) позволяют задать справедливость распределения доступа. По умолчанию (fair = false) используется неблагоприятный для порядка «быстрый» режим, который может давать большую пропускную способность. При fair = true ожидающие потоки получают доступ в порядке очереди.
Возвращаемые значения и исключения кратко:
lock(),unlock()- возвращают void;unlock()может привести кIllegalMonitorStateException.lockInterruptibly(),tryLock(long, TimeUnit)- могут броситьInterruptedException.tryLock(),tryLock(long, TimeUnit)- возвращаютboolean.
Когда применяется
Применение целесообразно при необходимости явного контроля порядка захвата и освобождения блокировки, при работе с Condition (await/signal), при необходимости поддерживать справедливость или при сложной логике синхронизации, которую нельзя выразить с помощью ключевого слова synchronized.
Короткие примеры использования
Пример 1 - базовая блокировка и разблокировка
import java.util.concurrent.locks.ReentrantLock;
public class BasicLock {
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try {
System.out.println("Критическая секция");
} finally {
lock.unlock();
}
}
}
Критическая секция
Пример 2 - tryLock() без блокировки
import java.util.concurrent.locks.ReentrantLock;
public class TryLockExample {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
if (lock.tryLock()) {
try {
System.out.println("Получил блокировку");
} finally {
lock.unlock();
}
} else {
System.out.println("Не удалось получить блокировку");
}
}
}
Получил блокировку
Пример 3 - tryLock с таймаутом
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockTimeout {
public static void main(String[] args) throws Exception {
ReentrantLock lock = new ReentrantLock();
lock.lock(); // имитируем занятую блокировку
Thread t = new Thread(() -> {
try {
if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
try { System.out.println("Тред получил блокировку"); } finally { lock.unlock(); }
} else {
System.out.println("Тред не дождался блокировки");
}
} catch (InterruptedException e) {
System.out.println("Тред прерван");
}
});
t.start();
Thread.sleep(1000);
lock.unlock();
t.join();
}
}
Тред не дождался блокировки
Пример 4 - lockInterruptibly()
import java.util.concurrent.locks.ReentrantLock;
public class InterruptibleExample {
public static void main(String[] args) throws Exception {
ReentrantLock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(() -> {
try {
System.out.println("Ожидание блокировки");
lock.lockInterruptibly();
try { System.out.println("Получено"); } finally { lock.unlock(); }
} catch (InterruptedException e) {
System.out.println("Ожидание прервано");
}
});
t.start();
Thread.sleep(200);
t.interrupt();
lock.unlock();
t.join();
}
}
Ожидание блокировки Ожидание прервано
Аналоги в Java и их особенности
- synchronized - встроенный механизм языка. Проще в использовании и безопасен для большинства задач, но не предоставляет Condition, справедливость и гибкие tryLock/timeout возможности.
- ReentrantReadWriteLock - разделяет чтение и запись: несколько читателей или один писатель. Предпочтительно при высоком соотношении чтений к записам.
- StampedLock - более продвинутый механизм для случаев с оптимистичным чтением и возможностью апгрейда блокировки; эффективен при специфичных шаблонах доступа.
- Semaphore - подсчетная семафорная блокировка; позволяет ограничивать количество параллельных исполнений, не привязана к владельцу.
Выбор зависит от требований: для простоты и автоматического освобождения при выходе из блокировки предпочтительнее synchronized; для сложной координации потоков и использования Conditions - ReentrantLock или ReentrantReadWriteLock.
Аналоги в других языках и отличия
Python
import threading
lock = threading.RLock()
lock.acquire()
try:
print('Критическая секция')
finally:
lock.release()
Критическая секция
Разница: RLock похож на ReentrantLock; acquire может принимать таймаут и реагирует на прерывания потоков по-иному, поскольку в Python нет прерывания по InterruptedException.
Go
package main
import (
"fmt"
"sync"
)
var mu sync.Mutex
func main() {
mu.Lock()
fmt.Println("Критическая секция")
mu.Unlock()
}
Критическая секция
Go предлагает sync.Mutex, не реентрантный по умолчанию; для реентрантности требуется иная логика.
C#
using System;
using System.Threading;
class Program {
static object sync = new object();
static void Main() {
lock(sync) {
Console.WriteLine("Критическая секция");
}
}
}
Критическая секция
В C# ключевое слово lock использует Monitor. Есть Mutex, SemaphoreSlim и другие механизмы. Reentrancy контролируется Monitor.
JavaScript
JS в браузере однопоточный; в Node.js возможны worker threads и shared memory с Atomics. Часто используют асинхронные mutex-реализации на промисах:
class Mutex {
constructor(){ this._q = Promise.resolve(); }
lock(){
let p = this._q;
let release;
this._q = new Promise(res => release = res);
return p.then(() => release);
}
}
(async ()=>{
const m = new Mutex();
const release = await m.lock();
console.log('Критическая секция');
release();
})();
Критическая секция
PHP
В чистом PHP нет встроенных потоков в стандартной установке. При использовании расширения pthreads доступен Mutex. В веб-контексте часто применяются внешние механизмы: блокировки в БД или файловые лocks.
SQL
В реляционных БД есть свои механизмы блокировок и advisory locks (например, pg_try_advisory_lock), которые обеспечивают координацию между процессами, а не между потоками JVM.
Kotlin
Kotlin на JVM использует те же примитивы, что Java. Kotlin coroutines используют другие стратегии синхронизации (Mutex из kotlinx.coroutines) для неблокирующего ожидания.
Типичные ошибки и последствия
1. Пропуск unlock в finally
ReentrantLock lock = new ReentrantLock();
lock.lock();
// если забыть finally, при исключении блокировка останется
// и другие потоки заблокируются навсегда
// неправильный код
if (true) throw new RuntimeException("ошибка");
lock.unlock();
Программа может зависнуть; другие потоки не получат блокировку
2. Вызов unlock без владения
ReentrantLock lock = new ReentrantLock();
try {
lock.unlock();
} catch (Exception e) {
System.out.println(e.getClass().getSimpleName());
}
IllegalMonitorStateException
3. Возможная потеря производительности при справедливом режиме
Установка new ReentrantLock(true) гарантирует порядок, но снижает пропускную способность по сравнению со стандартным режимом.
4. Deadlock при неверном порядке захвата нескольких блокировок
ReentrantLock a = new ReentrantLock();
ReentrantLock b = new ReentrantLock();
// Поток 1: захват a затем b
// Поток 2: захват b затем a
// Возможна ситуация взаимной блокировки
Взаимная блокировка потоков; программа зависает
5. Ожидание без реакции на прерывания
При использовании lock() поток не реагирует на прерывания до получения блокировки; если требуется отменяемое ожидание, следует использовать lockInterruptibly() или tryLock с таймаутом.
Изменения в реализации и смежные релизы
API ReentrantLock присутствует с введения пакета java.util.concurrent (Java 5). Сигнатуры методов lock, lockInterruptibly, tryLock и unlock не претерпели существенных изменений в последних версиях JDK. В Java 8 и последующих релизах появились дополнительные высокопроизводительные альтернативы, например StampedLock. Внутренние оптимизации JVM могли менять производительность реализации, но совместимость API сохраняется.
Расширенные примеры и ситуации
Пример 1 - демонстрация реентрантности
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantDemo {
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try {
System.out.println("Первый захват");
nested();
} finally { lock.unlock(); }
}
static void nested(){
lock.lock(); // тот же поток может повторно захватить
try { System.out.println("Второй захват"); }
finally { lock.unlock(); }
}
}
Первый захват Второй захват
Пример 2 - использование Condition для очереди ожидания
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition cond = lock.newCondition();
private boolean ready = false;
public void awaiter() throws InterruptedException {
lock.lock();
try {
while (!ready) {
cond.await();
}
System.out.println("Продолжил после сигнала");
} finally { lock.unlock(); }
}
public void signaler() {
lock.lock();
try {
ready = true;
cond.signalAll();
} finally { lock.unlock(); }
}
public static void main(String[] args) throws Exception {
ConditionExample ex = new ConditionExample();
Thread t = new Thread(() -> {
try { ex.awaiter(); } catch (InterruptedException e) { }
});
t.start();
Thread.sleep(100);
ex.signaler();
t.join();
}
}
Продолжил после сигнала
Пример 3 - избегание deadlock с tryLock и таймаутом
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class DeadlockAvoid {
static ReentrantLock a = new ReentrantLock();
static ReentrantLock b = new ReentrantLock();
static boolean tryAcquireBoth(ReentrantLock first, ReentrantLock second) throws InterruptedException {
if (!first.tryLock(500, TimeUnit.MILLISECONDS)) return false;
try {
if (!second.tryLock(500, TimeUnit.MILLISECONDS)) return false;
try { return true; } finally { second.unlock(); }
} finally { first.unlock(); }
}
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
try { System.out.println(tryAcquireBoth(a, b)); } catch (InterruptedException e) {}
});
Thread t2 = new Thread(() -> {
try { System.out.println(tryAcquireBoth(b, a)); } catch (InterruptedException e) {}
});
t1.start(); t2.start();
t1.join(); t2.join();
}
}
true true
Пример 4 - прерываемое ожидание блокировки
import java.util.concurrent.locks.ReentrantLock;
public class InterruptibleDemo {
public static void main(String[] args) throws Exception {
ReentrantLock lock = new ReentrantLock();
lock.lock();
Thread t = new Thread(() -> {
try {
lock.lockInterruptibly();
try { System.out.println("Получено"); } finally { lock.unlock(); }
} catch (InterruptedException e) { System.out.println("Прервано во время ожидания"); }
});
t.start();
Thread.sleep(100);
t.interrupt();
lock.unlock();
t.join();
}
}
Прервано во время ожидания
Пример 5 - справедливый и несчастливый режимы (наблюдение порядка)
import java.util.concurrent.locks.ReentrantLock;
public class FairVsUnfair {
public static void main(String[] args) throws Exception {
ReentrantLock fair = new ReentrantLock(true);
ReentrantLock unfair = new ReentrantLock(false);
// Поведение можно наблюдать запуская несколько потоков, которые пытаются захватить и печатать порядок.
System.out.println("Справедливость: fair vs unfair описана в документации; в тестах fair даёт порядок очереди");
}
}
Справедливость: fair vs unfair описана в документации; в тестах fair даёт порядок очереди
Приведённые примеры иллюстрируют общие и продвинутые сценарии: реентрантность, использование Condition, избежание deadlock с таймаутами и прерываемыми ожиданиями.
джава ReentrantLock.lock() function comments
- джава ReentrantLock.lock() - аргументы и возвращаемое значение
- Функция java ReentrantLock.lock() - описание
- ReentrantLock.lock() - примеры
- ReentrantLock.lock() - похожие методы на java
- ReentrantLock.lock() на javascript, c#, python, php
- ReentrantLock.lock() изменения java
- Примеры ReentrantLock.lock() на джава