0Pricing
Java Academy · Урок

ReadWriteLock для сценариев чтения и записи

Используйте ReadWriteLock, чтобы разрешить параллельное чтение и обеспечить эксклюзивную запись в кэше.

«ReadWriteLock для сценариев чтения и записи» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Задача читателей и писателей

Несколько потоков могут безопасно одновременно читать общие данные. Но запись требует исключительного доступа: параллельное чтение и запись недопустимы. ReadWriteLock моделирует это правило: несколько параллельных читателей ИЛИ один эксклюзивный писатель.

import java.util.concurrent.locks.*;

ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock  = rwLock.readLock();
Lock writeLock = rwLock.writeLock();

Блокировка чтения: общий доступ

Несколько потоков могут одновременно удерживать блокировку чтения, пока ни один поток не удерживает блокировку записи:

class ReadableCache {
    private final Map<String, String> cache = new HashMap<>();
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    
    String get(String key) {
        lock.readLock().lock();
        try {
            return cache.get(key); // concurrent reads OK
        } finally {
            lock.readLock().unlock();
        }
    }
}

Блокировка записи: исключительный доступ

Одновременно блокировку записи может удерживать только один поток. Во время записи все читатели и другие писатели блокируются:

class WritableCache extends ReadableCache {
    private final ReadWriteLock wLock = new ReentrantReadWriteLock();
    private final Map<String, String> data = new HashMap<>();
    
    void put(String key, String value) {
        wLock.writeLock().lock();
        try {
            data.put(key, value); // exclusive write
        } finally {
            wLock.writeLock().unlock();
        }
    }
}

Полный пример кэша

Потокобезопасный кэш, в котором чтения преобладают, а записи выполняются редко:

class Cache<K,V> {
    private final Map<K,V> map = new HashMap<>();
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
    V get(K key) {
        lock.readLock().lock();
        try { return map.get(key); }
        finally { lock.readLock().unlock(); }
    }
    
    void put(K key, V value) {
        lock.writeLock().lock();
        try { map.put(key, value); }
        finally { lock.writeLock().unlock(); }
    }
    
    int size() {
        lock.readLock().lock();
        try { return map.size(); }
        finally { lock.readLock().unlock(); }
    }
}

Понижение режима блокировки

ReadWriteLock поддерживает понижение режима блокировки: захватить блокировку записи → захватить блокировку чтения → освободить блокировку записи. Это позволяет потоку перейти от исключительной записи к общему чтению, не освобождая блокировку:

ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
Lock r = rwl.readLock(), w = rwl.writeLock();

w.lock(); // acquire write
try {
    // modify data
    r.lock(); // acquire read WHILE holding write
} finally {
    w.unlock(); // release write, now holding read only
}
try {
    // safely read the just-written data
} finally {
    r.unlock();
}

Блокировку чтения нельзя повысить

Повышение блокировки (чтение → запись) НЕ поддерживается. Если поток, удерживающий блокировку чтения, пытается захватить блокировку записи, возникает взаимная блокировка: блокировка записи ждёт всех читателей, включая этот же поток:

// DEADLOCK: Do NOT do this
lock.readLock().lock();
try {
    lock.writeLock().lock(); // DEADLOCK — waits for readLock to release
} finally {
    lock.readLock().unlock();
}

Когда ReadWriteLock полезен

ReadWriteLock полезен, если:

  • операций чтения значительно больше, чем операций записи;
  • операции чтения занимают заметное время, например выполняют сложные запросы.

Он может снизить производительность, если записи выполняются часто (блокировка записи блокирует всех читателей) или если операции очень быстрые (издержки двух объектов блокировки).

Сравнение ReadWriteLock и synchronized

synchronized/ReentrantLock → только один поток одновременно (даже параллельные читатели блокируются).
ReadWriteLock → несколько параллельных читателей и один эксклюзивный писатель. В сценариях с преобладанием чтения он обеспечивает более высокую пропускную способность чтения.

// synchronized: only one thread reads at a time
synchronized String get(String key) { return cache.get(key); }

// ReadWriteLock: many threads can read simultaneously
String get2(String key) {
    lock.readLock().lock();
    try { return cache.get(key); }
    finally { lock.readLock().unlock(); }
}

Особенности производительности

Захват и освобождение двух объектов блокировки добавляют издержки по сравнению с одной блокировкой. Прежде чем считать ReadWriteLock более быстрым, измерьте производительность. В сценариях с преобладанием чтения и длительными операциями преимущество очевидно. Для быстрых рабочих нагрузок с преобладанием записи простой ReentrantLock может оказаться быстрее.

Альтернатива ConcurrentHashMap

Для простых операций с отображением ConcurrentHashMap часто работает быстрее, чем HashMap + ReadWriteLock, поскольку использует внутренние блокировки на уровне сегментов без глобальной конкуренции:

// Often better than HashMap + ReadWriteLock for simple operations:
Map<String, String> concurrent = new ConcurrentHashMap<>();
concurrent.put("key", "value"); // thread-safe, no explicit lock
String v = concurrent.get("key"); // thread-safe

Справедливость блокировки чтения

По умолчанию ReentrantReadWriteLock работает в несправедливом режиме (читатели могут не давать писателям получить доступ). Используйте конструктор со справедливым режимом, чтобы предотвратить голодание писателей:

// Fair: waiting writers are served before new readers
ReentrantReadWriteLock fairLock = new ReentrantReadWriteLock(true);
System.out.println(fairLock.isFair()); // true

Быстрая проверка

В приложении с преобладанием чтения какое ключевое преимущество даёт ReadWriteLock по сравнению с простым синхронизированным методом?

Итоги: ReadWriteLock

Главное:

  • Несколько параллельных чтений ИЛИ одна эксклюзивная запись — одновременно оба режима невозможны
  • Используйте readLock.lock()/unlock() для общих операций чтения
  • Используйте writeLock.lock()/unlock() для эксклюзивных операций записи
  • Понижение блокировки поддерживается; повышение (чтение → запись) приводит к взаимной блокировке
  • Лучше всего подходит для сценариев с преобладанием чтения и нетривиальными операциями; для простых отображений используйте ConcurrentHashMap

Часто задаваемые вопросы

Урок «ReadWriteLock для сценариев чтения и записи» бесплатный?

Да — полный текст урока «ReadWriteLock для сценариев чтения и записи» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.

Чему я научусь в уроке «ReadWriteLock для сценариев чтения и записи»?

Используйте ReadWriteLock, чтобы разрешить параллельное чтение и обеспечить эксклюзивную запись в кэше. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Java Academy?

Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «ReadWriteLock для сценариев чтения и записи»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Java Academy?

Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. ReentrantLock и synchronized
  2. ReadWriteLock для сценариев чтения и записи
  3. Атомарные переменные: обновления без блокировок
  4. StampedLock и оптимистичное чтение
← Назад к Java Academy