乐观读是“不阻塞+后校验”,需 validate(stamp) 验证有效性,失败后必须重读并获取新 stamp;tryOptimisticRead() 返回的 stamp 不可 unlock,仅能 validate;它适用于轻量、幂等、字段少的读场景。

乐观读不是“无锁”,而是“不阻塞+后校验”——多数读操作跳过锁竞争,但必须 validate 后才能用数据,漏掉这步就可能读到脏值。
为什么 tryOptimisticRead() 返回的 stamp 不能直接用来 unlock
因为 tryOptimisticRead() 根本没加锁,它只是原子读取当前版本戳(stamp),不改变锁状态,所以没有对应的 unlock 操作。试图对它调用 unlockRead(stamp) 会抛 IllegalMonitorStateException。
-
validate(stamp)是唯一合法的后续操作:它只检查该 stamp 是否仍有效(即自获取后未发生写) - 只有 validate 失败后降级为
readLock(),才需要配对unlockRead(stamp) - 写操作返回的 stamp 才能用于
unlockWrite(stamp)
validate 失败后必须重读,且不能复用旧 stamp
一旦 validate(stamp) 返回 false,说明期间发生了写,所有之前基于该 stamp 读出的字段值都不可信——哪怕只改了无关字段,JVM 也不保证这些字段的可见性边界。
- 降级后调用
readLock(),必须重新读取所有依赖字段(不能只读被修改的那一个) - 不能把旧 stamp 再传给
validate(),它已经失效;新 stamp 要从readLock()返回 - 如果业务允许容忍短暂不一致(如监控指标),可选择放弃重试,直接返回旧值——但这是业务决策,不是锁机制兜底
乐观读只适合轻量、幂等、字段少的读场景
StampedLock 的乐观读不是通用替代品,它对读操作有隐式约束:字段越少、计算越快、重试代价越低,收益越明显。
- 读取超过 3~4 个字段时,validate 失败概率显著上升,频繁降级反而比直接用
readLock()更慢 - 涉及复杂对象引用(如
list.size()或map.get(k))必须小心:这些方法内部可能触发同步或修改状态,破坏乐观读前提 - 字段必须是基本类型或 final 引用(如
String、BigInteger),非 final 对象字段需额外 volatile 或安全发布保障 - 不要在乐观读路径里做 I/O、锁等待、或调用可能阻塞的方法
写操作会立即使所有未 validate 的乐观读 stamp 失效
每次 writeLock() 成功并进入临界区,都会原子更新内部 stamp 值——这个动作本身不阻塞乐观读线程,但会让所有已发出但尚未 validate 的 stamp 瞬间失效。
- 这意味着:乐观读的“窗口期”极短,本质是 CPU 级别的快速读 + 单次版本比对
- 如果读逻辑耗时稍长(比如含简单计算但跨多个指令周期),validate 失败率会陡增
- 写线程不因乐观读而延迟,这是它解决写饥饿的关键,但也意味着读线程要自己承担校验成本
真正难的不是写对 tryOptimisticRead() 和 validate(),而是判断哪些字段组合、多大概率、多长耗时,才值得走这条路径。实际压测中,同一段代码在 10 读 1 写和 100 读 1 写下行为可能完全不同——stamp 失效不是 bug,是设计使然。


















