StampedLock и оптимистичное чтение
Применяйте режим оптимистичного чтения StampedLock для высокопроизводительных нагрузок, преимущественно связанных с чтением.
«StampedLock и оптимистичное чтение» — бесплатный урок Java Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Что такое StampedLock
Появившийся в Java 8 класс StampedLock расширяет концепции ReadWriteLock дополнительным режимом оптимистичного чтения. Операции возвращают длинную метку, которая используется для освобождения блокировки или проверки оптимистичного чтения.
import java.util.concurrent.locks.*;
StampedLock sl = new StampedLock();
// Exclusive write:
long stamp = sl.writeLock();
try {
// write data
} finally {
sl.unlockWrite(stamp);
}
// Shared read:
stamp = sl.readLock();
try {
// read data
} finally {
sl.unlockRead(stamp);
}Режим оптимистичного чтения
При оптимистичном чтении блокировка не захватывается — вместо этого просто получается метка. После чтения проверьте метку. Если проверка не пройдена (писатель успел изменить данные), перейдите к настоящей блокировке чтения:
StampedLock sl2 = new StampedLock();
double x = 0, y = 0;
// Try optimistic read first:
long stamp = sl2.tryOptimisticRead();
double localX = x, localY = y; // read values
if (!sl2.validate(stamp)) {
// A write occurred — fall back to read lock
stamp = sl2.readLock();
try {
localX = x;
localY = y;
} finally {
sl2.unlockRead(stamp);
}
}
// use localX and localYПолный пример двумерной точки
Классический пример StampedLock — двумерная точка с оптимистичным чтением:
class Point {
private double x, y;
private final StampedLock lock = new StampedLock();
void move(double dx, double dy) {
long stamp = lock.writeLock();
try { x += dx; y += dy; }
finally { lock.unlockWrite(stamp); }
}
double distanceFromOrigin() {
long stamp = lock.tryOptimisticRead();
double cx = x, cy = y;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try { cx = x; cy = y; }
finally { lock.unlockRead(stamp); }
}
return Math.sqrt(cx * cx + cy * cy);
}
}Преобразование блокировки
StampedLock поддерживает преобразование между режимами блокировки:
StampedLock sl3 = new StampedLock();
// Read → Write upgrade attempt:
long readStamp = sl3.readLock();
long writeStamp = sl3.tryConvertToWriteLock(readStamp);
if (writeStamp != 0) {
// Successfully upgraded!
// ... write ...
sl3.unlockWrite(writeStamp);
} else {
// Upgrade failed — release read and reacquire write
sl3.unlockRead(readStamp);
writeStamp = sl3.writeLock();
// ... write ...
sl3.unlockWrite(writeStamp);
}Когда оптимистичное чтение наиболее полезно
Оптимистичное чтение особенно эффективно, если:
- операции чтения выполняются намного чаще операций записи;
- снимок данных помещается в несколько локальных переменных (без создания объекта);
- конкуренция при записи невысока (проверка редко завершается неудачей).
Если записи выполняются часто, оптимистичные чтения постоянно переходят к полноценным блокировкам чтения и не дают преимуществ.
StampedLock и ReadWriteLock
Главные различия:
- StampedLock: добавляет оптимистичное чтение; НЕ допускает повторного захвата; не поддерживает условные переменные
- ReentrantReadWriteLock: допускает повторный захват; поддерживает условные переменные; не имеет оптимистичного режима
StampedLock работает быстрее при низкой конкуренции записи. ReentrantReadWriteLock безопаснее и предоставляет больше возможностей.
StampedLock НЕ допускает повторного захвата
В отличие от ReentrantLock, StampedLock НЕ допускает повторного захвата. Поток, пытающийся получить уже удерживаемый им режим, попадёт во взаимную блокировку:
StampedLock sl4 = new StampedLock();
long s1 = sl4.readLock();
// DO NOT: sl4.readLock() again — deadlocks with non-reentrant!tryWriteLock и tryReadLock
Неблокирующие попытки захвата возвращают 0 при неудаче:
StampedLock sl5 = new StampedLock();
long stamp = sl5.tryWriteLock();
if (stamp != 0) {
try {
System.out.println("Got write lock");
} finally {
sl5.unlockWrite(stamp);
}
} else {
System.out.println("Write lock unavailable");
}Использование в качестве ReadWriteLock
StampedLock может вернуть представление в виде стандартного ReadWriteLock для совместимости:
StampedLock sl6 = new StampedLock();
Lock readLock = sl6.asReadLock();
Lock writeLock = sl6.asWriteLock();
ReadWriteLock rwView = sl6.asReadWriteLock();
// These provide standard Lock interface without optimistic modeПредставление о сравнении производительности
Результаты измерений обычно показывают:
- сильное преобладание чтения (99% чтений): StampedLock > ReadWriteLock > synchronized
- смешанная нагрузка (70% чтений): ReadWriteLock ≈ StampedLock
- преобладание записи: чаще выигрывают synchronized или ReentrantLock (меньше издержек)
Перед оптимизацией всегда измеряйте производительность на реалистичных рабочих нагрузках.
Недействительность метки после освобождения
После освобождения блокировки с помощью метки эта метка становится недействительной. Использование старой метки после освобождения приводит к неопределённому поведению — всегда получайте новую метку при каждом захвате блокировки.
StampedLock sl7 = new StampedLock();
long stamp = sl7.writeLock();
sl7.unlockWrite(stamp);
// stamp is now invalid — don't use it:
// sl7.unlockWrite(stamp); // throws IllegalMonitorStateException or corrupts stateБыстрая проверка
Оптимистичное чтение с помощью tryOptimisticRead() и последующая проверка validate(stamp) возвращают false. Что код должен сделать дальше?
Итоги: StampedLock
Главное:
- Три режима: оптимистичное чтение (без блокировки), блокировка чтения и блокировка записи
- tryOptimisticRead() + validate() — попытка чтения без затрат на захват блокировки
- При неудачной проверке переходите к блокировке чтения
- Повторный захват НЕ поддерживается — повторный захват тем же потоком может привести к взаимной блокировке
- Условные переменные не поддерживаются — при необходимости используйте ReentrantReadWriteLock
Часто задаваемые вопросы
Урок «StampedLock и оптимистичное чтение» бесплатный?
Да — полный текст урока «StampedLock и оптимистичное чтение» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «StampedLock и оптимистичное чтение»?
Применяйте режим оптимистичного чтения StampedLock для высокопроизводительных нагрузок, преимущественно связанных с чтением. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «StampedLock и оптимистичное чтение»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- ReentrantLock и synchronized
- ReadWriteLock для сценариев чтения и записи
- Атомарные переменные: обновления без блокировок
- StampedLock и оптимистичное чтение