Зачем после оптимистичного чтения через StampedLock нужна проверка валидности stamp?
Проверка validate(stamp) нужна потому, что оптимистичное чтение не блокирует писателей и само по себе не гарантирует согласованность данных. Если писатель захватил StampedLock во время чтения, stamp становится недействительным: прочитанный набор полей нужно считать ненадёжным и повторить чтение под обычной read-блокировкой.
Обычная блокировка чтения защищает данные, но при большом количестве читателей всё равно требует координации с писателями. StampedLock предоставляет более дешёвый режим для сценариев, где чтения преобладают над изменениями: читатель сначала пробует получить снимок без блокировки.
Такой подход повышает пропускную способность на малоконфликтных чтениях, но переносит ответственность за проверку согласованности на код читателя. Это компромисс между производительностью и простотой обычной блокировки.
Оптимистичный stamp не означает, что поток получил право безопасно читать данные. Пока читатель загружает несколько полей, писатель может изменить объект между этими загрузками, и читатель получит комбинацию значений из разных состояний.
Без последующей проверки приложение может использовать внутренне противоречивый снимок: например, координаты, относящиеся к разным версиям объекта. Ошибка особенно опасна, если результат чтения используется для расчётов, проверок инвариантов или принятия решений.
tryOptimisticRead() возвращает stamp, описывающий текущую версию состояния блокировки. Этот режим не блокирует писателя и не является полноценным захватом read-lock.
Читатель сначала загружает все нужные поля в локальные переменные, затем вызывает validate(stamp). Метод проверяет, не захватывал ли писатель блокировку после получения stamp. Если проверка успешна, локальный снимок можно использовать; если нет — значения нужно отбросить и получить заново под readLock().
Проверять stamp следует после чтения всех связанных полей и до использования снимка. После успешного validate защита не продолжается: писатель может изменить данные сразу после проверки, поэтому локальные значения нужно рассматривать как уже полученный снимок.
Все операции записи должны выполняться через тот же StampedLock. validate не обнаружит изменения, сделанные напрямую или под другим механизмом синхронизации. StampedLock также не является реентерабельным, поэтому сложные вложенные сценарии требуют осторожного проектирования.
В сервисе часто читаются связанные параметры состояния, а обновляются они редко. Использование synchronized даёт простую и надёжную защиту, но каждый читатель конкурирует с другими потоками за монитор. ReentrantReadWriteLock позволяет нескольким читателям работать одновременно, однако чтение всё равно связано с операциями захвата и освобождения read-lock.
StampedLock с оптимистичным чтением уменьшает накладные расходы при отсутствии конфликтов: большинство читателей получают данные без блокировки, а при пересечении с записью повторяют операцию под read-lock. Выбор оправдан, если чтения действительно преобладают, снимок можно безопасно повторить, а команда готова строго соблюдать протокол блокировки.
Если запись происходит часто или согласованность чтения критична и повторная попытка дорога, обычный read-lock может быть предпочтительнее. Он проще для проверки и сразу предоставляет более ясную гарантию защиты критической секции.
validate(stamp) вернуть true, а данные измениться сразу после проверки?Да. Успешная проверка подтверждает состояние только относительно момента проверки и не удерживает блокировку после неё. Поэтому нужно сохранить значения в локальные переменные и использовать именно этот снимок; если требуется защита последующих операций над общим состоянием, следует удерживать обычный read-lock.
Нет. Оптимистичный режим не захватывает read-lock, поэтому писатель может начать работу параллельно с чтением. В этом его преимущество по пропускной способности, но именно из-за этого чтение может оказаться недействительным и потребовать повторения.
StampedLock?Протокол синхронизации будет нарушен. validate отслеживает только конфликты, проходящие через данный StampedLock, поэтому внешняя запись может остаться незамеченной, а обычные поля могут быть прочитаны с некорректной видимостью или в несогласованном состоянии. Все связанные чтения и записи должны использовать один и тот же механизм защиты.