浅谈CAS,一篇就够了

这篇具有很好参考价值的文章主要介绍了浅谈CAS,一篇就够了。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

cas,Java,java,cas,乐观锁
wshanshi:喵桑说,我总结完CAS就带我去吃羊蝎子火锅…干饭那必须整起啊…

一、什么是CAS?

CAS:Compare and Swap。从字面意义上来说,就是先进行比较,然后替换。

它是乐观锁思想的一种实现,尤其是在并发量大的业务场景下保证单个实例的原子性,使用较为频繁。java类库中java.util.concurrent.atomic包下一些方法,也均使用CAS处理。

二、悲观锁与乐观锁

CAS是乐观锁思想的一种体现,那乐观锁和悲观锁有什么区别呢?

2.1、悲观锁

悲观锁常见使用是synchronized修饰的代码块或者方法。

在操作数据之前加锁,直到数据操作完成,锁被释放之后,其它线程才可以操作该数据。比如,mysql数据库锁就是悲观锁。

2.2、乐观锁

数据操作不加锁,每次提交之前获取最新值与原获取值进行对比,数据未变更时操作,否则自旋。

2.3、区别

  • 乐观锁是并行的,悲观锁是串行的。

乐观锁:
cas,Java,java,cas,乐观锁
悲观锁:
cas,Java,java,cas,乐观锁

  • 乐观锁实质并未“加锁”,悲观锁是加了锁的(synchronized)。

三、CAS原理

Compare and Swap,比较并替换。说白了就是:在操作提交之前,与原获取到的值先进行比较,判断这个值有没有被修改。如果未被修改,操作修改。如果已被修改,则重新获取值,提交之前再比较…

举个栗子:关于《损友经常在群里偷偷改我的头衔》这件事。

线程a获取到我的信息,并操作将我的头衔由“三婶”改为了“王胖虎”。
cas,Java,java,cas,乐观锁
之后线程b获取到我的头衔为“王胖虎”,又修改了“王胖虎”为“三婶”。
cas,Java,java,cas,乐观锁
这是正常的一种非并发流程的体现,我们再来看下面一种情况:

假设线程a和线程b均获取到我的名字“三婶”。且线程a操作修改了“三婶”为“王胖虎”。
cas,Java,java,cas,乐观锁

若a线程成功操作之后,线程b在修改提交前获取名称,发现实际读到的头衔是“王胖虎”。
cas,Java,java,cas,乐观锁
对比预期值(三婶),发现"三婶"!=“王胖虎”(被修改了)。这时按照CAS原理,就会再次获取预期值(此时预期值为:王胖虎),且提交前获取内存值,进行对比…判断是否一致…

CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,需要替换的新值B。

计算规则是:当需要更新一个变量的值的时候,仅当变量的预期值A(原获取)和内存地址V(提交前获取)中实际值相同的时候,才会把内存地址V对应的值替换成B。

如下图示例:

cas,Java,java,cas,乐观锁

四、核心Unsafe类库

该类可直接操作内存,所以效率高。且Unsafe类和常量均使用final修饰,单例模式实现,不可继承。通过下图所示静态方法getUnsafe进行实例化,实例化在static块中操作的。

cas,Java,java,cas,乐观锁

cas,Java,java,cas,乐观锁
cas,Java,java,cas,乐观锁

Unsafe可以设置读写某个属性,如下图所示。

cas,Java,java,cas,乐观锁

volatile 保证了不同线程对共享变量操作的可见性,也就是说一个线程修改了 volatile 修饰的变量,当修改后的变量写回主内存时,其他线程能立即看到最新的值。

cas,Java,java,cas,乐观锁

五、CAS优缺点

优点:在并发问题不严重的时候,性能方面比synchronized要快。

缺点:不能确保代码块的原子性。因为CAS机制确保的是一个变量的原子性操作,并不能保证整个代码块的原子性。如果多个变量共同进行原子性的更新操作,就需要用lock或者synchronized了。

5.1、自旋

假设线程a和线程b均获取到我的名字“三婶”。线程a操作修改了“三婶”为“王胖虎”,之后线程b在提交前获取名称,发现读到的是“王胖虎”。对比不一致,就会再次获取。

假如这个时候有个线程c把“王胖虎”改为了“胖虎”,线程b读取时又不一致了。这个时候就会一直获取,一直对比…

这种现象称为“自旋”。
cas,Java,java,cas,乐观锁

5.2、ABA情况

还拿上述的栗子来说:

假设线程a和线程b均获取到我的名字“三婶”。然后线程a操作修改了“三婶”为“王胖虎”。

如果这个时候有个线程c成功操作:将“王胖虎”改为了“三婶”。线程b在提交前获取名称,发现读到的是“三婶”,它会以为“这个值并没有发生变化”。

但实质上,这个值可能是多次被修改后,恰巧变为了原始值的一种情况,也就是所谓的ABA.

cas,Java,java,cas,乐观锁

六、如何避免ABA情况?

6.1、加版本号

每次操作compareAndSwap后给数据的版本号加1,再次compareAndSwap的时候不仅比较数据,也比较版本号,值相同,若是版本号不同,就不执行成功。

java.util.concurrent.atomic包中提供了AtomicStampedReference来解决该问题。

cas,Java,java,cas,乐观锁
AtomicStampedReference 内部维护了一个 Pair的数据结构:reference(数据体)、stamp(版本)两个部分。该数据结构用volatile修饰,保证了线程可见性。
cas,Java,java,cas,乐观锁

核心方法为:compareAndSet方法。该方法中,expectedReference:表示预期值,newReference:表示新的值,expectedStamp:表示预期版本号,newStamp表示新的版本号。

cas,Java,java,cas,乐观锁
从数据和版本号两个方面来判断传入的参数是否符合 Pair 的预期,有一个不符合就返回false。
cas,Java,java,cas,乐观锁

可以看到,这里底层也是使用了cas。预期值为“三婶”,版本号为0。新值为“王胖虎”,新版本号为1。

cas,Java,java,cas,乐观锁

而casPair实质上调用的是UNSAFE.compareAndSwapObject()方法。

cas,Java,java,cas,乐观锁

由此可见,AtomicStampedReference是通过加版本号来解决ABA问题的。对于加版本号,compareAndSwapObject只能对比交互一个对象,所以将数据和版本号放到一个对象里就可以解决问题了。文章来源地址https://www.toymoban.com/news/detail-839147.html

到了这里,关于浅谈CAS,一篇就够了的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java面向对象】多态的详细介绍,简单易懂,看这一篇就够了

    A: 方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承的基础之上的。简单来说,多态是具有表现多种形态的能力的特征。 消除类型之间的耦合关系 可替代性 可扩充性 接口性 灵活性 简化性 重载式多态在编译时已经确定好了。方法名相同而参数

    2024年01月20日
    浏览(51)
  • 计组一篇就够了

    1.2.1计算机工作过程 1.2.4计算机性能指标 机器字长、指令字长、存储字长的区别和联系是什么? 机器字长:计算机能直接处理的二进制数据的位数,机器字长一般等于内部寄存器的大小, 它决定了计算机的运算精度 。 指令字长:一个指令字中包含的二进制代码的位数。 存

    2024年02月01日
    浏览(28)
  • Spark入门(一篇就够了)

    声明 : 本文为大数据肌肉猿公众号的《5W字总结Spark》的学习笔记,如有侵权请联系本人删除! Spark 知识图谱如下: Spark 是当今大数据领域最活跃、最热门、最高效的大数据通用计算平台之一 。 Hadoop 之父 Doug Cutting 指出:Use of MapReduce engine for Big Data projects will decline, repla

    2024年02月03日
    浏览(27)
  • 搞懂flyaway一篇就够了

    Flyway是一个用于数据库迁移的开源工具,它可以帮助开发人员轻松地管理数据库架构的变化。Flyway通过迁移来更新数据库,迁移可以使用特定于数据库的SQL语法或者用于高级数据库转换的Java编写。Flyway支持两种类型的迁移:有版本的迁移和可重复的迁移。有版本的迁移具有唯

    2024年02月03日
    浏览(31)
  • python入门,一篇就够了

    函数必须写注释:文档注释格式 \\\'\\\'\\\'注释内容\\\'\\\'\\\' 参数中的等号两边不要用空格 相邻函数用两个空行隔开 小写 + 下划线 函数名 模块名 实例名 驼峰法 类名 结构化类型,有一系列的属性和类型 标量类型,此对象无可访问的内部对象 python 中,整型相除默认是浮点型 建议:使用

    2024年02月15日
    浏览(30)
  • 学习SpringSecurity这一篇就够了

    案例源码地址:https://gitee.com/gzl_com/spring-security.git 1.1、概要 Spring Security 是 Spring 家族中的成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。 安全方面的两个主要区域是“ 认证 ”和“ 授权 ”。在Web 应用又称之为 用户认证 和 用户授权 两个部

    2024年02月11日
    浏览(36)
  • docker入门,这一篇就够了。

    Docker容器虚拟化平台。 今天跟大家分享一下我的docker学习历程,也算是我的独特的复习笔记,我会在这一篇中讲清楚docker几乎所有的功能。不过也是我第一次写,而且是一篇两万多字的长文,花了我半个月里所有的休闲娱乐时间,所以写的不好的地方请大家见谅,也请在评论

    2024年02月03日
    浏览(39)
  • 学会大数据基础,一篇就够了

    1 Hadoop的三大组件 1) HDFS分布式文件管理系统 超大数据存储 流式存储 2) MapRuduce分布式并行编程模型 3) Yarn 资源管理和调度器 2 其他组件 4 HBase 实时读写 非关系型数据库 分布式列式数据库 基于HDFS数据存储 5 Hive 数据仓库 SQL语句转换为mapreduce任务 6 Flume 日志采集聚合 7 Sqoop 传

    2024年02月04日
    浏览(27)
  • CSS基础——看这一篇就够了

    目录 一、CSS简介 1.CSS是什么? 2.CSS的作用 3.CSS的构成 二、CSS选择器 1.基础选择器 (1).标签选择器 (2)类选择器 (3)标签选择器 (4) 通配符选择器 2.复合选择器 (1)后代选择器(包含选择器) (2)子选择器 (3)并集选择器 (4)伪类选择器  三、基本属性 1.字体属性

    2024年02月09日
    浏览(44)
  • 精通线程池,看这一篇就够了

    当我们运用多线程技术处理任务时,需要不断通过new的方式创建线程,这样频繁创建和销毁线程,会造成cpu消耗过多。那么有没有什么办法 避免频繁创建线程 呢? 当然有,和我们以前学习过多连接池技术类似,线程池通过提前创建好线程保存在线程池中, 在任务要执行时取

    2023年04月17日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包