Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Типы блокировок (Locks) в Java
Blokirovki (Locks) - это механизм синхронизации потоков для защиты общих ресурсов от одновременного доступа. Java предоставляет различные типы локов с разными характеристиками и применением.
1. Intrinsic Lock (встроенная блокировка через synchronized)
Это самый базовый и исторический вид блокировки в Java:
public class IntrinsicLockExample {
private int counter = 0;
// Блокировка на уровне метода
public synchronized void incrementCounter() {
counter++;
}
// Блокировка на объекте
public void criticalSection() {
synchronized(this) {
counter++;
}
}
// Статическая блокировка (на класс)
public synchronized static void staticMethod() {
// блокировка Class объекта
}
}
Характеристики:
- Неограниченное время ожидания
- Нет возможности проверить статус блокировки
- Выходит автоматически при выходе из scope
- Fair scheduling НЕ гарантирован
2. ReentrantLock - переиспользуемая блокировка
Это явная блокировка, которая может быть переиспользована одним потоком:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int counter = 0;
public void increment() {
lock.lock(); // получить блокировку
try {
counter++;
} finally {
lock.unlock(); // ВСЕГДА отпустить в finally
}
}
// Один тот же поток может захватить несколько раз
public void reentrantExample() {
lock.lock(); // счётчик = 1
try {
lock.lock(); // счётчик = 2 (один поток может)
try {
// критическая секция
} finally {
lock.unlock(); // счётчик = 1
}
} finally {
lock.unlock(); // счётчик = 0
}
}
// Попытка захватить с timeout
public boolean incrementWithTimeout() {
try {
if (lock.tryLock(java.util.concurrent.TimeUnit.SECONDS, 5)) {
try {
counter++;
return true;
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
// Проверить если блокировка свободна
public boolean incrementIfFree() {
if (lock.tryLock()) { // не блокирует, сразу возвращает
try {
counter++;
return true;
} finally {
lock.unlock();
}
}
return false;
}
}
Преимущества над synchronized:
tryLock()- неблокирующая попыткаtryLock(timeout)- с таймаутомlockInterruptibly()- может быть прервана- Более гибкое управление
- Fair locking возможен
3. ReentrantReadWriteLock - блокировка для чтения/записи
Позволяет множеству потоков читать одновременно, но только одному писать:
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private int value = 0;
// Много потоков могут читать одновременно
public int getValue() {
lock.readLock().lock();
try {
return value;
} finally {
lock.readLock().unlock();
}
}
// Только один поток может писать
public void setValue(int newValue) {
lock.writeLock().lock();
try {
value = newValue;
} finally {
lock.writeLock().unlock();
}
}
// Downgrade из write lock в read lock
public int updateAndGet() {
lock.writeLock().lock();
try {
value++;
// Было бы хорошо здесь downgrade, но ReentrantReadWriteLock не поддерживает
return value;
} finally {
lock.writeLock().unlock();
}
}
}
// Use case: кэш, который часто читается но редко обновляется
public class CacheWithReadWriteLock<K, V> {
private final Map<K, V> cache = new HashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public V get(K key) {
lock.readLock().lock(); // множество потоков могут читать
try {
return cache.get(key);
} finally {
lock.readLock().unlock();
}
}
public void put(K key, V value) {
lock.writeLock().lock(); // только один поток пишет
try {
cache.put(key, value);
} finally {
lock.writeLock().unlock();
}
}
}
4. StampedLock - оптимистичная блокировка (Java 8+)
Оплата с оптимистичным чтением для максимальной производительности:
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private final StampedLock lock = new StampedLock();
private int x = 0, y = 0;
// Оптимистичное чтение - не захватывает блокировку
public int readValues() {
long stamp = lock.tryOptimisticRead(); // почти бесплатно
int localX = x; // читаем данные
int localY = y;
// Проверяем, не изменились ли данные во время чтения
if (!lock.validate(stamp)) {
// Данные менялись, нужно перечитать с настоящей блокировкой
stamp = lock.readLock(); // захватываем read lock
try {
localX = x;
localY = y;
} finally {
lock.unlockRead(stamp);
}
}
return localX + localY;
}
// Эксклюзивное (exclusive) чтение и запись
public void writeValues(int newX, int newY) {
long stamp = lock.writeLock(); // эксклюзивная блокировка
try {
x = newX;
y = newY;
} finally {
lock.unlockWrite(stamp);
}
}
// Upgrade из read в write
public void incrementX() {
long stamp = lock.readLock();
try {
while (true) {
long writeStamp = lock.tryConvertToWriteLock(stamp);
if (writeStamp != 0L) {
try {
x++;
stamp = writeStamp;
break;
} finally {
lock.unlockWrite(writeStamp);
}
} else {
lock.unlockRead(stamp);
stamp = lock.writeLock();
}
}
} finally {
if (lock.isReadLocked(stamp)) {
lock.unlockRead(stamp);
}
}
}
}
Когда использовать:
- Очень частые чтения, редкие записи
- Данные читаются быстро (без вложенных вызовов)
- Валидация успешна в большинстве случаев
5. Semaphore - семафор (ограничение доступа)
Ограничивает количество потоков, могущих одновременно получить доступ:
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// Только 3 потока могут одновременно использовать ресурс
private final Semaphore semaphore = new Semaphore(3);
public void accessLimitedResource() throws InterruptedException {
semaphore.acquire(); // уменьшает счётчик
try {
// работаем с ограниченным ресурсом (например, connection pool)
System.out.println("Accessing resource");
Thread.sleep(1000);
} finally {
semaphore.release(); // увеличивает счётчик
}
}
// Пример: ограничение connection pool
public class ConnectionPool {
private final Semaphore available = new Semaphore(10); // 10 соединений
private final Queue<Connection> pool = new ConcurrentLinkedQueue<>();
public Connection getConnection() throws InterruptedException {
available.acquire();
Connection conn = pool.poll();
return conn != null ? conn : createNewConnection();
}
public void returnConnection(Connection conn) {
pool.offer(conn);
available.release();
}
}
}
6. CountDownLatch - ожидание нескольких потоков
Ожидает завершения нескольких событий:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
// Ждём пока 3 потока завершат работу
CountDownLatch latch = new CountDownLatch(3);
// Запускаем 3 рабочих потока
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " started");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
latch.countDown(); // сигнализирует о завершении
System.out.println(Thread.currentThread().getName() + " finished");
}).start();
}
latch.await(); // блокируется до countDown == 0
System.out.println("All threads finished");
}
}
7. CyclicBarrier - синхронизация потоков на барьере
Все потоки ждут друг друга на точке синхронизации:
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3);
for (int i = 0; i < 3; i++) {
final int id = i;
new Thread(() -> {
try {
System.out.println("Thread " + id + " waiting at barrier");
barrier.await(); // ждёт пока все 3 потока сюда приходят
System.out.println("Thread " + id + " passed barrier");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
Сравнительная таблица
Тип | Переиспользуемость | Fair | Timeout | Use Case
---
synchronized | Нет | Нет | Нет | Простые случаи
Reentrant | Да | Да | Да | Гибкость нужна
ReadWrite | Да | Да | Да | Много читает, редко пишет
Stamped | Да | Нет | Нет | Очень много чтений
Semaphore | - | - | Да | Ограничение доступа
CountDown | - | - | Да | Ждать событий
Barrier | Циклический | - | Да | Синхронизация волн
Правила использования локов
- Всегда используй try-finally или try-with-resources
- Избегай вложенных локов (deadlock risk)
- Удерживай лок минимальное время
- Предпочитай ReentrantLock synchronized'у
- Для чтения/записи используй ReadWriteLock
- StampedLock только для очень частых чтений