怎么通过 StampedLock 的乐观读锁提升多读少写场景的性能

老伟大大_4085

老伟大大_4085

2026-04-12

876人浏览

原创

乐观读能减少写线程阻塞,因为tryOptimisticRead不加锁、不参与竞争,仅读取版本戳,validate校验是否被写;读多写少时多数读跳过锁开销,写线程可随时获取writeLock,避免写饥饿。

怎么通过 stampedlock 的乐观读锁提升多读少写场景的性能

乐观读锁为什么能减少写线程阻塞

因为 tryOptimisticRead 不加锁,也不参与锁竞争,它只读取一个版本戳(stamp),后续靠 validate 校验是否被写过。当读多写少时,绝大多数读操作能跳过锁开销,同时写线程仍可随时获取 writeLock —— 这就打破了 ReentrantReadWriteLock 中“一有读锁、写就必须等”的死局,避免写饥饿。

常见错误现象:直接用 tryOptimisticRead 读完就返回,不校验 validate,导致读到脏数据;或在校验失败后没 fallback 到悲观读,而是硬 retry,造成逻辑错乱或无限循环。

  • 乐观读仅适用于「读取+简单判断」类场景,比如检查某个状态是否为 true、读取一个数值做比较
  • 不能用于需要强一致性的复合操作(如先读 x 再读 y,再算 x+y)
  • 校验失败后应立即转为 readLock + validate 或直接 writeLock,而不是自旋重试

怎么正确使用 tryOptimisticRead + validate 流程

核心是两段式:先无锁读,再校验;失败则降级。这不是“自动重试机制”,而是一次性决策路径。

示例场景:判断账户余额是否足够扣款

long stamp = sl.tryOptimisticRead();
double currentBalance = balance; // 无同步读取
if (!sl.validate(stamp)) {
    // 乐观读失效,降级为悲观读
    stamp = sl.readLock();
    try {
        currentBalance = balance;
    } finally {
        sl.unlockRead(stamp);
    }
}
return currentBalance >= amount;

注意点:

  • tryOptimisticRead 返回 0 表示当前有写锁活跃,可直接跳过乐观路径
  • validate 必须在读取共享变量「之后、使用之前」立刻调用,中间不能插入其他可能修改状态的操作
  • 悲观读锁拿到的 stamp 必须配对 unlockRead,否则资源泄漏

为什么不能把乐观读当成“轻量版读锁”滥用

乐观读不是性能银弹。它绕过了内存屏障和锁队列,代价是放弃一致性保证。JVM 不保证该读操作对其他线程可见,也不保证指令重排序被禁止 —— 所以它只适合「最终一致性可接受」的读场景。

典型误用:

  • 在乐观读路径中调用外部方法(如 log.info()),导致校验前实际已发生不可逆副作用
  • 读取多个字段后统一校验,但字段间存在隐含约束(如 version 和 data 需配套),validate 只能保证 stamp 未变,无法保证字段组合逻辑有效
  • 在循环中反复 tryOptimisticRead + validate,相当于手动实现 CAS 自旋,反而比直接 readLock 更耗 CPU

性能影响:乐观读本身几乎零开销,但频繁 validate 失败会触发更多悲观读,吞吐反而下降;实测中 validate 失效率超过 5% 就该重新评估是否适合该场景。

StampedLock 的 stamp 必须显式管理

writeLock 和 readLock 返回的 stamp 是解锁唯一凭证,且 StampedLock 不可重入 —— 同一线程重复 writeLock 会导致死锁,这点和 ReentrantReadWriteLock 完全不同。

容易踩的坑:

  • 忘记保存 stamp,或保存成局部变量后在异步回调里使用,导致 unlockWrite(0) 或 unlockRead(0) —— 实际无效果,锁永远不释放
  • 用同一个 stamp 多次调用 unlockWrite,会抛 IllegalMonitorStateException
  • 在 try-finally 外提前 return,跳过 unlock,造成锁泄漏(尤其在嵌套判断中)

建议始终把 stamp 声明为 final 局部变量,并严格配对 try-finally;不要跨方法传递 stamp,更不要存入对象字段。

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

11937

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

9022

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

7732

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1264

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

1008

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1516

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

3129

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20371

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1375

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.1万人学习