Java 性能调优实战
刘超
前金山软件技术经理
59174 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
开篇词 (1讲)
模块一 · 概述 (2讲)
结束语 (1讲)
Java 性能调优实战
15
15
1.0x
00:00/00:00
登录|注册

13 | 多线程之锁优化(中):深入了解Lock同步锁的优化方法

乐观读
悲观读
写锁
读锁
StampedLock
RRW
读写锁分离
存在的缺陷导致没有被广泛应用
没有被广泛应用的原因
锁分离
减少锁占用时间
减小锁粒度
StampedLock
促使系统的并发性能达到最佳
利用Lock锁的灵活性
降低锁竞争
思考题
总结

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

你好,我是刘超。
今天这讲我们继续来聊聊锁优化。上一讲我重点介绍了在 JVM 层实现的 Synchronized 同步锁的优化方法,除此之外,在 JDK1.5 之后,Java 还提供了 Lock 同步锁。那么它有什么优势呢?
相对于需要 JVM 隐式获取和释放锁的 Synchronized 同步锁,Lock 同步锁(以下简称 Lock 锁)需要的是显示获取和释放锁,这就为获取和释放锁提供了更多的灵活性。Lock 锁的基本操作是通过乐观锁来实现的,但由于 Lock 锁也会在阻塞时被挂起,因此它依然属于悲观锁。我们可以通过一张图来简单对比下两个同步锁,了解下各自的特点:
从性能方面上来说,在并发量不高、竞争不激烈的情况下,Synchronized 同步锁由于具有分级锁的优势,性能上与 Lock 锁差不多;但在高负载、高并发的情况下,Synchronized 同步锁由于竞争激烈会升级到重量级锁,性能则没有 Lock 锁稳定。
我们可以通过一组简单的性能测试,直观地对比下两种锁的性能,结果见下方,代码可以在Github上下载查看。
通过以上数据,我们可以发现:Lock 锁的性能相对来说更加稳定。那它与上一讲的 Synchronized 同步锁相比,实现原理又是怎样的呢?
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了Lock同步锁的优化方法,重点介绍了其相对于Synchronized同步锁的优势以及实现原理。通过对读写锁ReentrantReadWriteLock和StampedLock的详细讲解,展示了针对读多写少场景的优化方案。文章强调了降低锁竞争对于优化锁的重要性,以及利用Lock锁的灵活性来降低锁竞争的方法。StampedLock作为一种优化方案,具有更高的效率和避免了CPU占用性能问题,但其没有被广泛应用的原因和存在的缺陷仍需进一步探讨。整体而言,本文为读者提供了全面的技术视角和实践应用,对于理解和应用锁的优化方法具有重要参考价值。

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

全部留言(61)

  • 最新
  • 精选
  • Liam
    StampLock不支持重入,不支持条件变量,线程被中断时可能导致CPU暴涨

    作者回复: 回答很全面

    2019-06-18
    3
    66
  • 我知道了嗯
    可重入锁是什么?另外什么场景下会使用到?

    作者回复: 可重入锁是指在同一个线程在前面方法中已获取锁了,再进入该线程的其他方法获取锁,此时不会因为之前获取锁而阻塞。 平时我们很少遇到这种情况,例如在A方法中使用了对象锁,B方法中也使用了该对象锁,平时一般都是分别调用A方法和B方法,而后面由于业务需求刚好需要在A方法中调用B方法,此时就会需要锁支持可重入性。

    2019-06-20
    2
    23
  • -W.LI-
    老师好!读写锁那个流程图看不太明白,没有写线程的时候,判断不是当前线程在读就会进入CLF阻塞等待。 问题1:不是可以并发读的嘛?按这图读线程也要阻塞等待的意思么? 问题二:CLF阻塞队列里是读写线程公用的么?队列里,读写交替出现。那不就没法并发读了么?

    作者回复: 第一个问题,这里有一个公平锁和非公平锁的情况,如果是公平锁,即使无锁竞争的情况下,也会进入阻塞队列中排队获取锁;否则,会立即CAS获取到读锁。 第二个问题,是公用的,这里同样涉及到了公平锁和非公平锁,读写线程对于程序来说都是一样的。如果是非公平锁,如果没有锁竞争的情况下CAS获取锁成功,是无需进入阻塞队列。如果是公平锁,都会进入阻塞队列。

    2019-06-18
    17
  • 密码123456
    为什么?因为锁不可重入?

    作者回复: 是的,StampedLock不支持可重入。如果在一些需要重入的代码中使用StampedLock,会导致死锁、饿死等情况出现。

    2019-06-18
    11
  • 王圣军
    老师这里说的公平锁和非公平锁让我想起两者是获取方式不同,非公平锁是首先就CAS来获取一次,成功就拿到锁,失败就放入队列;公平锁不会有这步操作,直接放入队列

    作者回复: 是的

    2019-12-27
    10
  • 码农Kevin亮
    请问老师,在读写锁的场景中,我在读操作时为什么还要加锁?直接读不就可以了?如果担心数据不刷新,那在变量加volatile是不是就可以满足?请解惑

    作者回复: 在某些场景中,ReentrantLock这种读写锁能保证数据的强一致性。假设我们有两个对象x,y被volatile修饰,在A线程调用写入方法,x被写入及时更新到缓存中,而y没有,此时B线程刚好读取x,y的值,此时读取的x值是被修改过的,而y值还是原来的值,即x,y存在数据不一致的可能。

    2020-03-18
    4
    7
  • 张三丰
    获取读锁的流程图有问题吧,应该是判断写锁是否为当前线程,而不是判断读锁。

    作者回复: 对的,理解很到位,发现了问题

    2020-04-10
    5
  • -W.LI-
    StampedLock在写多读少的时候性能会很差吧

    作者回复: 是的,写多读少的性能没有优势。

    2019-06-18
    5
  • 你好旅行者
    老师我有几个问题: 1.在ReentrantLock中,state这个变量,为0的时候表示当前的锁是没有被占用的。这个时候线程应该用CAS尝试修改state变量的值对锁进行抢占才对呀,为什么在您的图里当state=0的时候还需要判断是否为当前线程呢? 2.老师提到读写锁在读多写少的情况下会使得写线程遭遇饥饿问题,那我是不是只需要将锁设置为公平锁,这样先申请写锁的线程就可以先获得锁,从而避免饥饿问题呢? 3.StampedLock中引入了一个stamp版本对版本进行控制,那么对这个stamp变量进行写入的时候是否需要使用CAS操作?如果不是,那如何保证对stamp变量的读写是线程安全的呢? 谢谢老师!

    作者回复: 第一个问题,是老师笔误,搞错方向了,现在已更正。 第二个问题,如果读多写少的情况下,即使是公平锁,也是需要长时间等待,不是想获取时就能立即获取到锁。StampedLock如果是处于乐观读时,写锁是可以随时获取到锁。 第三个问题,StampedLock源码中存在大量compareAndSwapObject操作来保证原子性。

    2019-06-18
    4
  • 奋斗的小白鼠
    老师,lock锁中的线程阻塞进行的上下文切换会设计系统内核态和用户态的转换吗?啥时候会引起系统内核态和用户态转换成啊?.io流编程中会出现吗

    作者回复: lock锁阻塞不会带来进程间的上下文切换,IO流存在的,在09讲中讲到了

    2019-11-28
    3
收起评论
显示
设置
留言
61
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部