stampedlock 乐观读失败后需转悲观读,即先用 tryoptimisticread() 获取 stamp 并验证,无效时调用 readlock() 获取读锁、二次读取并 unlockread() 释放,确保数据一致性。

StampedLock 的乐观读失败后转悲观读,核心在于用 tryOptimisticRead() 获取戳记(stamp),检查是否有效,无效时再用 readLock() 获取真实读锁 —— 这是避免写竞争导致数据不一致的关键步骤。
乐观读的基本流程
乐观读不加锁,只在读取前获取一个“戳记”,读完后验证戳记是否仍有效。若期间有写操作发生,戳记失效,说明数据可能已变,需降级为悲观读重新读取。
- 调用
StampedLock.tryOptimisticRead()得到一个 long 类型的 stamp - 立即读取共享变量(注意:不能有 I/O、阻塞、耗时计算等不确定行为)
- 调用
StampedLock.validate(stamp)判断戳记是否仍有效 - 若返回
false,说明期间发生过写操作,乐观读结果不可靠
失败后转悲观读的正确写法
验证失败后,不能直接重试乐观读,而应主动获取读锁,确保读取期间无写入干扰。获取锁后建议再次读取数据(因为写操作可能已更新值),并记得释放锁。
- 调用
StampedLock.readLock()获取读锁,返回新的 stamp - 在
try块中完成读取逻辑(推荐二次读取,避免依赖之前可能过期的数据) - 必须用
StampedLock.unlockRead(stamp)释放锁,否则会造成锁泄漏 - 注意:
readLock()是阻塞式,会等待当前写锁释放,但不会饿死写线程(StampedLock 公平性弱于 ReentrantReadWriteLock,但写优先)
典型代码结构示例
以下是一个安全读取字段 value 的常见模式:
long stamp = sl.tryOptimisticRead();
int current = value; // 快速读取
if (!sl.validate(stamp)) {
// 乐观读失败,升级为悲观读
stamp = sl.readLock();
try {
current = value; // 再次读取,确保一致性
} finally {
sl.unlockRead(stamp);
}
}
// 后续使用 current 变量做计算或返回
注意:两次读取之间没有修改逻辑,且 value 应为 final 或 volatile 修饰(仅限基本类型或不可变对象),否则仍可能存在可见性问题。
容易忽略的关键细节
StampedLock 的乐观读不是万能的,它对使用方式很敏感,几个常见坑点需警惕:
- 乐观读期间禁止调用可能被阻塞的方法(如
System.out.println、网络请求、锁等待),否则会拖长验证窗口,增加失败概率 - 不能在乐观读段落里修改任何状态(包括局部变量以外的副作用),否则验证失败后回退逻辑会变得不可控
- 写操作必须用
writeLock()+unlockWrite()配对,否则乐观读的戳记无法正确感知变更 - StampedLock 不支持重入,同一线程重复获取读锁或写锁会导致死锁,务必保证锁与解锁严格配对
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











