Java 并发编程实战
王宝令
资深架构师
72485 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
学习攻略 (1讲)
Java 并发编程实战
15
15
1.0x
00:00/00:00
登录|注册

18 | StampedLock:有没有比读写锁更快的锁?

Bug示例
锁的降级和升级
代码模板
中断操作导致CPU飙升
不支持条件变量
不支持重入
数据库的乐观锁
乐观读的操作是无锁的
乐观读的方式
获取/释放写锁
获取/释放悲观读锁
乐观读
悲观读锁
写锁
读锁
课后思考
总结
使用注意事项
进一步理解乐观读
性能优势
示例代码
三种锁模式
StampedLock
参考文章

该思维导图由 AI 生成,仅供参考

上一篇文章中,我们介绍了读写锁,学习完之后你应该已经知道“读写锁允许多个线程同时读共享变量,适用于读多写少的场景”。那在读多写少的场景中,还有没有更快的技术方案呢?还真有,Java 在 1.8 这个版本里,提供了一种叫 StampedLock 的锁,它的性能就比读写锁还要好。
下面我们就来介绍一下 StampedLock 的使用方法、内部工作原理以及在使用过程中需要注意的事项。

StampedLock 支持的三种锁模式

我们先来看看在使用上 StampedLock 和上一篇文章讲的 ReadWriteLock 有哪些区别。
ReadWriteLock 支持两种模式:一种是读锁,一种是写锁。而 StampedLock 支持三种模式,分别是:写锁悲观读锁乐观读。其中,写锁、悲观读锁的语义和 ReadWriteLock 的写锁、读锁的语义非常类似,允许多个线程同时获取悲观读锁,但是只允许一个线程获取写锁,写锁和悲观读锁是互斥的。不同的是:StampedLock 里的写锁和悲观读锁加锁成功之后,都会返回一个 stamp;然后解锁的时候,需要传入这个 stamp。相关的示例代码如下。
final StampedLock sl =
new StampedLock();
// 获取/释放悲观读锁示意代码
long stamp = sl.readLock();
try {
//省略业务相关代码
} finally {
sl.unlockRead(stamp);
}
// 获取/释放写锁示意代码
long stamp = sl.writeLock();
try {
//省略业务相关代码
} finally {
sl.unlockWrite(stamp);
}
StampedLock 的性能之所以比 ReadWriteLock 还要好,其关键是 StampedLock 支持乐观读的方式。ReadWriteLock 支持多个线程同时读,但是当多个线程同时读的时候,所有的写操作会被阻塞;而 StampedLock 提供的乐观读,是允许一个线程获取写锁的,也就是说不是所有的写操作都被阻塞。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Java 1.8引入了一种性能更优的锁机制——StampedLock,它支持写锁、悲观读锁和乐观读三种模式。乐观读是无锁的,性能比传统读锁更佳,类似于数据库的乐观锁。然而,StampedLock不支持重入,悲观读锁和写锁不支持条件变量,且在阻塞时调用中断操作会导致CPU飙升。建议按照Java官方示例的模板来使用StampedLock。虽然StampedLock的使用看似复杂,但理解乐观锁原理后使用起来流畅。StampedLock的性能优势使其适用于读多写少的场景,可替代传统的ReadWriteLock。 此外,StampedLock支持锁的降级和升级,但建议慎重使用。文章中提供了一个代码示例,隐藏了一个Bug,读者可以思考并找出Bug出在哪里。StampedLock的灵活性和性能优势使其成为Java开发中的一个重要工具,但在使用过程中需要注意其特性和潜在的问题。 StampedLock的引入为Java开发者提供了更多选择,但也需要谨慎使用,以充分发挥其优势并避免潜在的问题。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 并发编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(78)

  • 最新
  • 精选
  • linqw
    课后思考题:在锁升级成功的时候,最后没有释放最新的写锁,可以在if块的break上加个stamp=ws进行释放

    作者回复: 👍

    2019-04-09
    5
    105
  • 等我先变个身
    乐观锁的想法是“没事,肯定没被改过”,于是就开心地获取到数据,不放心吗?那就再验证一下,看看真的没被改过吧?这下可以放心使用数据了。 我的问题是,验证完之后、使用数据之前,数据被其他线程改了怎么办?我看不出validate的意义。这个和数据库更新好像还不一样,数据库是在写的时候发现已经被其他人写了。这里validate之后也难免数据在进行业务计算之前已经被改掉了啊?

    作者回复: 改了就改了,读的数据是正确的一致的就可以了。如果这个规则不满足业务需求,可以总互斥锁。不同的锁用不同地方。

    2019-04-09
    8
    48
  • Grubby🐑
    老师,调用interrupt引起cpu飙高的原因是什么

    作者回复: 内部实现里while循环里面对中断的处理有点问题

    2019-04-09
    42
  • Presley
    老师,StampedLock 读模板,先通过乐观读或者悲观读锁获取变量,然后利用这些变量处理业务逻辑,会不会存在线程安全的情况呢? 比如,读出来的变量没问题,但是进行业务逻辑处理的时候,这时,读出的变量有可能发生变化了吧(比如被写锁改写了)?所以,当使用乐观读锁时,是不是等业务都处理完了(比如先利用变量把距离计算完),再判断变量是否被改写,如果没改写,直接return;如果已经改写,则使用悲观读锁做同样的事情。不过如果业务比较耗时,可能持有悲观锁的时间会比较长,不知道理解对不对

    作者回复: 两种场景,如果处理业务需要保持互斥,那么就用互斥锁,如果不需要保持互斥才可以用读写锁。一般来讲缓存是不需要保持互斥性的,能接受瞬间的不一致

    2019-04-09
    29
  • Grubby🐑
    bug是tryConvertToWriteLock返回的write stamp没有重新赋值给stamp

    作者回复: 👍

    2019-04-09
    2
    21
  • 发条橙子 。
    老师 , 我看事例里面成员变量都给了一个 final 关键字 。 请问这里给变量加 final的用意是什么 ,仅仅是为了防止下面方法中代码给他赋新的对象么 。 我在平常写代码中很少有给变量加 final 的习惯, 希望老师能指点一下 😄

    作者回复: 使用final是个好习惯

    2019-04-15
    2
    14
  • ban
    老师,你好, 如果我在前面long stamp = sl.readLock();升级锁后long ws = sl.tryConvertToWriteLock(stamp); 这个 stamp和ws是什么关系来的,是sl.unlockRead(是关stamp还是ws)。两者有什么区别呢

    作者回复: stamp和ws没关系,tryConvertToWriteLock(stamp)这个方法内部会释放悲观读锁stamp(条件是能够升级成功)。所以我们需要释放的是ws

    2019-04-13
    9
  • 南北少卿
    jdk源码StampedLock中的示例,if (ws != 0L) 时使用了stamp=ws

    作者回复: 👍

    2019-05-12
    7
  • 楊_宵夜
    王老师, 您好, 文章中的StampedLock模板, 只适用于单机应用吧? 如果是集群部署, 那还是得用数据库乐观锁, 是吗??

    作者回复: 是的

    2019-06-14
    5
  • 南北少卿
    这个获取锁返回的stamp,可以理解成上锁后的钥匙吗?

    作者回复: 这里类比很生动😄

    2020-06-19
    4
收起评论
显示
设置
留言
78
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部