StampedLock适合读多写少且延迟敏感场景,通过tryOptimisticRead获取stamp后立即读字段,再validate验证;若失败则降级readLock重试,确保一致性,但不可用于写操作或跨方法传递stamp。

StampedLock 适合读多写少且对延迟敏感的场景,用乐观读(tryOptimisticRead)可避免读锁开销,但必须配合 validate 验证结果有效性。
理解乐观读的核心机制
乐观读不加锁,只获取一个“戳记”(stamp),后续通过 validate(stamp) 判断期间是否有写操作发生。若返回 true,说明数据未被修改,读取结果可信;若为 false,需降级为悲观读锁重试。
关键点:
- 乐观读本身不阻塞、不改变同步状态,性能开销极低
- 它不保证读取过程原子性——你读的多个字段可能来自不同写入时刻,需自行确保逻辑一致性
- 不能在乐观读后直接调用写操作,也不支持重入或条件等待
典型使用模式:先乐观、再验证、必要时重试
以读取两个关联字段为例(如余额和状态):
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
立即学习“Java免费学习笔记(深入)”;
long stamp = lock.tryOptimisticRead();
int currentBalance = balance;
Status currentStatus = status;
if (!lock.validate(stamp)) {
// 发生了写操作,降级为悲观读
stamp = lock.readLock();
try {
currentBalance = balance;
currentStatus = status;
} finally {
lock.unlockRead(stamp);
}
}
// 此时 currentBalance 和 currentStatus 是一致快照(要么都来自乐观读,要么都来自悲观读)
注意:所有要读取的字段必须在 validate 前完成读取,否则验证失去意义。
何时不该用乐观读
以下情况建议直接用悲观读锁(readLock())或改用其他同步机制:
- 读取逻辑复杂、耗时较长(比如遍历集合、IO 等),validate 失败概率高,反复重试反而更慢
- 需要保证多个字段强一致性,且它们更新逻辑分散、无法原子维护(乐观读无法替代事务语义)
- 写操作非常频繁,乐观读几乎总是失败,退化成“先猜再锁”,增加额外开销
- 代码需要在 Java 8 之前运行(StampedLock 自 JDK 8 引入)
实用建议与避坑点
实际使用中要注意:
- stamp 是 long 类型,不要保存或跨方法传递,它只对当前读取瞬间有效
- 避免在乐观读分支里修改共享状态,哪怕只是临时变量赋值,也可能干扰 JIT 优化或引发意料外重排序
- 如果读取后要基于结果做写操作,必须重新获取写锁(乐观读 stamp 不能用于 unlock 或升级)
- 调试时可通过
lock.isWriteLocked()辅助判断当前是否被写占用,但生产环境慎用——它本身有开销且非原子

















