Gymterview
senior

Что такое StampedLock и чем он отличается от ReentrantReadWriteLock?

StampedLock — это блокировка из Java 8, которая поддерживает три режима доступа: запись, чтение и оптимистичное чтение. Обеспечивает более высокую производительность по сравнению с ReentrantReadWriteLock при преобладании операций чтения.

Три режима:

  1. Запись (write lock) — эксклюзивная блокировка.
  2. Чтение (read lock) — разделяемая блокировка.
  3. Оптимистичное чтение (optimistic read) — неблокирующее. Возвращается «штамп» (stamp), который проверяется на валидность. Если за время чтения произошла запись — штамп невалиден, нужно повторить чтение.
Код: StampedLock с оптимистичным чтением
class Point {
    private double x, y;
    private final StampedLock lock = new StampedLock();

    // Запись
    void move(double deltaX, double deltaY) {
        long stamp = lock.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    // Оптимистичное чтение — самый производительный способ
    double distanceFromOrigin() {
        long stamp = lock.tryOptimisticRead(); // Не блокирует!
        double currentX = x, currentY = y;     // Читаем данные

        if (!lock.validate(stamp)) {           // Проверяем: была ли запись?
            // Если была — переходим к пессимистичному чтению
            stamp = lock.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
}

Сравнение с ReentrantReadWriteLock:

Характеристика ReentrantReadWriteLock StampedLock
Оптимистичное чтение Нет Да
Реентрабельность Да Нет
Условия (Condition) Да Нет
Fairness policy Да Нет
Конвертация блокировок Нет Да (read -> write, optimistic -> read)
Производительность чтения Хорошая Отличная (с optimistic read)

Конвертация блокировок:

Код: повышение блокировки чтения до записи
long stamp = lock.readLock();
try {
    while (x == 0.0) {
        long writeStamp = lock.tryConvertToWriteLock(stamp);
        if (writeStamp != 0L) {
            stamp = writeStamp;
            x = 1.0;
            break;
        } else {
            lock.unlockRead(stamp);
            stamp = lock.writeLock();
        }
    }
} finally {
    lock.unlock(stamp);
}

Ключевые особенности:

  • Не реентрабелен — повторный захват из того же потока приведёт к deadlock.
  • Оптимистичное чтение — это не блокировка, а «снимок» состояния. Валидация обязательна.
  • Не реализует интерфейс Lock — собственный API на основе штампов (long stamp).
  • Лучше всего подходит для коротких критических секций с преобладанием чтения.

Частые ошибки:

  • Забытый finally — штамп нужно передавать при unlock, без finally — вечная блокировка.
  • Оптимистичное чтение для длинных операций — частые повторения могут быть дороже обычной блокировки.
  • Реентрабельный захват — deadlock без явной ошибки.
  • Неверный штамп в unlockIllegalMonitorStateException.

Аналогия: StampedLock с оптимистичным чтением — это как чтение расписания на вокзале. Вы подходите, читаете время (optimistic read), а затем проверяете, не перевернулся ли табло (validate). Если перевернулось — читаете заново. Если нет — используете данные. Это быстрее, чем ждать в очереди у окошка кассы (pessimistic read lock).

На собеседовании ключевой вопрос: «Когда StampedLock лучше ReentrantReadWriteLock?» Ответ: когда соотношение чтений к записям высокое (>10:1) и критические секции короткие. Главное ограничение — отсутствие реентрабельности.