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

14 | 多线程之锁优化(下):使用乐观锁优化并行操作

处理器实现原子操作
原子操作
LongAdder
CAS
ABA问题
读写差不多
读少写多
读多写少
优化
实现原理
定义
思考题
性能对比测试
乐观锁
如何使用乐观锁优化并行操作

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

你好,我是刘超。
前两讲我们讨论了 Synchronized 和 Lock 实现的同步锁机制,这两种同步锁都属于悲观锁,是保护线程安全最直观的方式。
我们知道悲观锁在高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增加系统的性能开销。那有没有可能实现一种非阻塞型的锁机制来保证线程的安全呢?答案是肯定的。今天我就带你学习下乐观锁的优化方法,看看怎么使用才能发挥它最大的价值。

什么是乐观锁

开始优化前,我们先来简单回顾下乐观锁的定义。
乐观锁,顾名思义,就是说在操作共享资源时,它总是抱着乐观的态度进行,它认为自己可以成功地完成操作。但实际上,当多个线程同时操作一个共享资源时,只有一个线程会成功,那么失败的线程呢?它们不会像悲观锁一样在操作系统中挂起,而仅仅是返回,并且系统允许失败的线程重试,也允许自动放弃退出操作。
所以,乐观锁相比悲观锁来说,不会带来死锁、饥饿等活性故障问题,线程间的相互影响也远远比悲观锁要小。更为重要的是,乐观锁没有因竞争造成的系统开销,所以在性能上也是更胜一筹。

乐观锁的实现原理

相信你对上面的内容是有一定的了解的,下面我们来看看乐观锁的实现原理,有助于我们从根本上总结优化方法。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

乐观锁是一种非阻塞型的锁机制,通过CAS算法实现原子操作,减少系统性能开销。在高并发写大于读的场景下,乐观锁LongAdder表现优异,将操作压力分散到多个变量值上,提高性能。然而,在对实时性要求较高的场景下,LongAdder并不能完全取代原子类。文章还对Synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock以及乐观锁LongAdder进行了性能测试,发现在不同读写场景下,各种锁的性能表现有所差异。读者在实际应用中需要根据具体场景选择合适的乐观锁优化方法。文章还提出了思考题,引发读者思考CAS操作中的ABA问题。

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

全部留言(33)

  • 最新
  • 精选
  • 张学磊
    变量的原值为A,当线程T读取后到更新前这段时间,可能被其他线程更新为B值后又更新回A值,到当线程T进行CAS操作时感知不到这个变化,依然可以更新成功;StampdLock通过过去锁时返回一个时间戳可以解决该问题。

    作者回复: 不仅回答了问题,还给出了解决方案,赞一个

    2019-06-20
    56
  • colin
    老师您好,cell数组里存数得是+1 -1这种操作值么? 还有,“LongAdder 在操作后的返回值只是一个近似准确的数值,但是 LongAdder 最终返回的是一个准确的数值”这句话中“操作后返回值”和“最终返回值”怎么理解?

    作者回复: 假设操作后立即要获取到值,这个值可能是一个不准确的值。如果我们等待所有线程执行完成之后去获取,这个值肯定是准确的值。一般在做统计时,会经常用到这种操作,实时展现的只要求一个近似值,但最终的统计要求是准确的。

    2019-06-20
    2
    24
  • gerry pang
    老师,我看源码中大量用for(;;),请问那它和while(true)之间有什么明显的性能区别吗?

    作者回复: 虽然都是无限循环,但for(;;)是无条件循环,而while(true)是有条件循环,for(;;)编译后的指令非常简单,而while(true)编译后的指令包含了跳转、判断等。

    2020-05-12
    4
    23
  • 风轻扬
    老师,ABA的问题,CAS最终关心的是:值是否是A。那ABA的影响是什么呢?

    作者回复: 我们假设一个队列来分析ABA问题,会更好理解。 如果一个队列有A\B\A三个数据,在线程1获取队列头节点数据A后,如果CAS发现数据没有变,则修改头节点A为A1,此时正好有一个线程删除了头节点A,又有另外一个线程也删除了后来成为头节点的B,此时头节点是依然是A,而此时第一个线程去修改A,这将导致实际修改的不是队列刚开始的那个节点A。

    2019-08-25
    22
  • crazypokerk
    LongAdder原理如下:将热点数据value被分离成多个单元的cell,每个cell独自维护内部的值,当前对象的实际值由cell[]数组中所有的cell累计合成。这样,热点就进行了有效的分离,提高了并行度,所以LongAdder虽然降低了并发竞争,但是却对实时更新的数据不友好。

    作者回复: 是的

    2019-06-20
    17
  • 左瞳
    根据你的测试结果,都是乐观锁最优,是不是线程变为100个或者以上,其他测试结果才会优于乐观锁?

    作者回复: 通常情况下,乐观锁的性能是要优于悲观锁,跟线程数量没有太大关系

    2019-06-26
    4
  • 码农Kevin亮
    这里想反馈一下,每个小节都讲得太绕了,老师可否直接点题,我越看越困惑: 1,关于“什么是乐观锁”。乐观锁=CAS?CAS不是属于无锁嘛,所以乐观锁=无锁? 2,关于“CAS的实现原理”。CAS是通过锁缓存来实现的,是吗?而synchronized是锁总线,是吗?

    作者回复: 1、乐观锁是一种概念,通过版本号来实现锁是一种乐观锁,而CAS是一种乐观锁的具体实现; 2、CAS是通过底层CPU缓存锁定实现的,这里的总线锁没有涉及到synchronized,只是之前老的CPU是根据总线锁来实现的,由于更新换代,目前使用的是性能更好的缓存锁。 建议多阅读几次文章。

    2019-08-06
    2
  • 文灏
    LongAdder 在操作后的返回值只是一个近似准确的数值, 但是 LongAdder 最终返回的是一个准确的数值. 那什么时候才能知道LongAdder现在返回的值是正确的了呢?

    作者回复: 例如,我们在做销量统计的时候,用到LongAdder 统计销量,我们只需要保证最终写入的销量,在以后查询是是准确的。具体的时间也许是毫秒之后能查到,也许是分钟之后,但我们只需要保证在写入之后,能最终统计之前写入的销量。

    2019-07-03
    2
  • 很有帮助,系统性的重新审视学习各个锁,顺带将老师的测试代码用JMH测试框架、面向对象化重构了下。 https://github.com/seasonsolt/lockTest,有助于自己进一步深度学习研究。

    作者回复: 赞

    2019-06-27
    1
  • z.l
    cas方法的三个参数是如何和cpu的缓存锁定机制联系到一起的呢?感觉没有理解,还请老师解答。

    作者回复: 原理就是,当某个处理器对缓存中的共享变量进行了操作,就会通知其它处理器放弃对存储该共享资源或者重新读取该共享资源。

    2019-06-23
    2
    1
收起评论
显示
设置
留言
33
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部