CAS原理分析及ABA问题详解

什么是CAS

CASCompare And Swap的缩写,翻译成中文就是比较并交换,其作用是让CPU比较内存中某个值是否和预期的值相同,如果相同则将这个值更新为新值,不相同则不做更新,也就是CAS是原子性的操作(读和写两者同时具有原子性),其实现方式是通过借助C/C++调用CPU指令完成的,所以效率很高。
CAS的原理很简单,这里使用一段Java代码来描述

public boolean compareAndSwap(int value, int expect, int update) {
//        如果内存中的值value和期望值expect一样 则将值更新为新值updateif (value == expect) {value = update;return true;} else {return false;}
}
复制代码

大致过程是将内存中的值、我们的期望值、新值交给CPU进行运算,如果内存中的值和我们的期望值相同则将值更新为新值,否则不做任何操作。这个过程是在CPU中完成的,这里不好描述CPU的工作过程,就拿Java代码来描述了。

Unsafe源码分析

Java是在Unsafe(sun.misc.Unsafe)类实现CAS的操作,而我们知道Java是无法直接访问操作系统底层的API的(原因是Java的跨平台性限制了Java不能和操作系统耦合),所以Java并没有在Unsafe类直接实现CAS的操作,而是通过**JDI(Java Native Interface)**本地调用C/C++语言来实现CAS操作的。
Unsafe有很多个CAS操作的相关方法,这里举例几个

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
复制代码

我们拿public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);进行分析,这个方法是比较内存中的一个值(整型)和我们的期望值(var4)是否一样,如果一样则将内存中的这个值更新为var5,参数中的var1是值所在的对象,var2是值在对象(var1)中的内存偏移量,参数var1和参数var2是为了定位出值所在内存的地址

Unsafe.java在这里发挥的作用有:

  1. 将对象引用、值在对象中的偏移量、期望的值和欲更新的新值传递给Unsafe.cpp
  2. 如果值更新成功则返回true给开发者,没有更新则返回false

Unsafe.cpp在这里发挥的作用有:

  1. 接受从Unsafe传递过来的对象引用、偏移量、期望的值和欲更新的新值,根据对象引用和偏移量计算出值的地址,然后将值的地址、期望的值、欲更新的新值传递给CPU
  2. 如果值更新成功则返回trueUnsafe.java,没有更新则返回false

CPU在这里发挥的作用:

  1. 接受从Unsafe.cpp传递过来的地址、期望的值和欲更新的新值,执行指令cmpxchg,比较地址中的值是否和期望的值一样,一样则将值更新为新的值,不一样则不做任何操作
  2. 将操作结果返回给Unsafe.cpp

CAS的缺点

CAS虽然高效的实现了原子性操作,但是也存在一些缺点,主要表现在以下三个方面。

ABA问题

在多线程场景下CAS会出现ABA问题,关于ABA问题这里简单科普下,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下

  1. 线程1,期望值为A,欲更新的值为B
  2. 线程2,期望值为A,欲更新的值为B

线程1抢先获得CPU时间片,而线程2因为其他原因阻塞了,线程1取值与期望的A值比较,发现相等然后将值更新为B,然后这个时候出现了线程3,期望值为B,欲更新的值为A,线程3取值与期望的值B比较,发现相等则将值更新为A,此时线程2从阻塞中恢复,并且获得了CPU时间片,这时候线程2取值与期望的值A比较,发现相等则将值更新为B,虽然线程2也完成了操作,但是线程2并不知道值已经经过了A->B->A的变化过程。

ABA问题带来的危害
小明在提款机,提取了50元,因为提款机问题,有两个线程,同时把余额从100变为50
线程1(提款机):获取当前值100,期望更新为50,
线程2(提款机):获取当前值100,期望更新为50,
线程1成功执行,线程2某种原因block了,这时,某人给小明汇款50
线程3(默认):获取当前值50,期望更新为100,
这时候线程3成功执行,余额变为100,
线程2从Block中恢复,获取到的也是100,compare之后,继续更新余额为50!!!
此时可以看到,实际余额应该为100(100-50+50),但是实际上变为了50(100-50+50-50)这就是ABA问题带来的成功提交。

解决方法: 在变量前面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A

循环时间长开销大

如果CAS操作失败,就需要循环进行CAS操作(循环同时将期望值更新为最新的),如果长时间都不成功的话,那么会造成CPU极大的开销。

这种循环也称为自旋

解决方法: 限制自旋次数,防止进入死循环。

只能保证一个共享变量的原子操作

CAS的原子操作只能针对一个共享变量。

解决方法: 如果需要对多个共享变量进行操作,可以使用加锁方式(悲观锁)保证原子性,或者可以把多个共享变量合并成一个共享变量进行CAS操作。

CAS的应用

我们知道CAS操作并不会锁住共享变量,也就是一种非阻塞的同步机制,CAS就是乐观锁的实现。

  1. 乐观锁 乐观锁总是假设最好的情况,每次去操作数据都认为不会被别的线程修改数据,所以在每次操作数据的时候都不会给数据加锁,即在线程对数据进行操作的时候,别的线程不会阻塞仍然可以对数据进行操作,只有在需要更新数据的时候才会去判断数据是否被别的线程修改过,如果数据被修改过则会拒绝操作并且返回错误信息给用户。
  2. 悲观锁 悲观锁总是假设最坏的情况,每次去操作数据时候都认为会被的线程修改数据,所以在每次操作数据的时候都会给数据加锁,让别的线程无法操作这个数据,别的线程会一直阻塞直到获取到这个数据的锁。这样的话就会影响效率,比如当有个线程发生一个很耗时的操作的时候,别的线程只是想获取这个数据的值而已都要等待很久。

Java利用CAS的乐观锁、原子性的特性高效解决了多线程的安全性问题,例如JDK1.8中的集合类ConcurrentHashMap、关键字volatileReentrantLock等。

参考

JAVA CAS原理深度分析
Java CAS 原理分析
什么是ABA问题?

原文地址:ddnd.cn/2019/03/13/…

转载于:https://juejin.im/post/5c87afa06fb9a049f1550b04

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/279922.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在Windows Mobile模拟器(Emulator)建立网络连接

因为想使用Windows Mobile Emulator进行网络通信程序的测试,所以找方法配置Emulator的网络连接。在网上找了一些文章,很多都说需要安装Virtual PC 2007. 例如下面的文章Enable Network Connection Windows Mobile 6 Emulator 如果需要 Virtual PC 2007 可…

api游戏编程鼠标选择拖动_如何选择合适的游戏鼠标

api游戏编程鼠标选择拖动You don’t need a gaming mouse to play PC games—just about any mouse with two buttons and a wheel will play anything you want it to. But that’s no reason to deny yourself the wonderful variety of gaming mouse designs on the market.…

iOS - 上架的APP 生成二维码下载

1.首先打开苹果App Store商店进入到里面,找到需要打开链接地址的应用程序,例如:百度。2. 在App Store商店里面先点击一下应用程序图标,再按一下…分享按钮。 3. 接着选择分享APP,再点击拷贝链接地址,将应用…

Rsa2加密报错java.security.spec.InvalidKeySpecException的解决办法

最近在和支付宝支付做个对接,Java项目中用到了RSA2进行加解密,在加密过程中遇到了错误: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence 代码执行到这句…

浅析领域驱动设计

1.概要DDD(Domain-driven design,模型驱动设计)是一种软件设计的指导思想,而非固定的一套公式化开发模板(这样就会导致网络上出现各种基于自己或业务上的理解而产出的DDD落地的实现,会让很想学习的开发者迷…

Delphi实现的透明阴影以及蒙版效果菜单

QQ2010的皮肤控件目前实现了一部分,看到有些软件的菜单,都有阴影,透明等效果,于是开始重新实现菜单控件,QQ2009版的菜单控件,是自己从TComponent继承了完全模拟实现的一个菜单,虽然实现了菜单控…

cortana搜索框_如何在Windows 10任务栏上隐藏Cortana搜索框

cortana搜索框One of the most talked about features in the latest version of Windows 10 was the Cortana personal assistant that is integrated directly into the taskbar. But what if you don’t want to waste all that taskbar space? 最新版本的Windows 10中最受…

Kotlin 基础 - 数据类型

一、Boolean 类型 Boolean 值有两个值,分别为 true 或 false。多数情况下,Kotlin 中的 Boolean 相当于 Java 中的基本类型 boolean,只有在必要的情况下才会装箱成为 Java 中的装箱类型 Boolean。这一切都是交由编译器来完成,我们无…

全框眼镜拆卸镜片方法分享

全框眼镜拆卸镜片方法分享http://www.iqiyi.com/w_19ru97p1n9.html 很多直接用手掰就成(眼镜布) 转载于:https://www.cnblogs.com/OceanF/p/9288411.html

发送http请求

public static String httpGetSend(String url) {String responseMsg "";HttpClient httpClient new HttpClient();GetMethod getMethod new GetMethod(url);// GET请求try {// http超时5秒httpClient.getHttpConnectionManager().getParams().setConnectionTimeo…

微软公布Entity Framework 8.0规划

微软.NET团队在博客上公布了有关 Entity Framework Core 8.0(也称为 EF Core 8 或 EF8)的未来规划。EF Core 8 是 EF Core 7 之后的下一个版本,这将是一个长期支持版本;计划于 2023 年 11 月与 .NET 8 同时发布。该公司表示&#…

roku能不能安装软件_如何阻止假期更改Roku主题

roku能不能安装软件Wondering why your Roku looks…different? Roku occasionally changes the background for its millions of users, something they call a “featured theme.” 想知道为什么您的Roku看起来...不同吗? Roku偶尔会改变其数百万用户的背景&…

助力AIoT,雅观科技发布空间智能化操作系统

雷锋网(公众号:雷锋网)消息,3月14日,雅观科技在上海举办了“「AI」悟及物 「柔」生万屋”2019雅观科技新品发布会,发布了空间智能化操作系统Akeeta、空间智能化柔性服务技术中台Matrix,以及基于两者开发的雅观智慧社区…

HTTP与HTTPS区别(详细)

转:http://blog.sina.com.cn/s/blog_6eb3177a0102x66r.html 1、减少http请求(合并文件、合并图片)2、优化图片文件,减小其尺寸,特别是缩略图,一定要按尺寸生成缩略图然后调用,不要在网页中用res…

Ajenti-Linux控制面板之自动化运维工具

ajenti http://ajenti.org/ https://github.com/ajenti/ajenti 源码 http://docs.ajenti.org/en/latest/ http://docs.ajenti.org/en/latest/man/install.html# 安装部署Fast remote access for every occasion Install once and never google for PuTTY downloads again. An…

MongoDB C# Driver 快速入门

MongoDB的官方C#驱动可以通过这个链接得到。链接提供了.msi和.zip两种方式获取驱动dll文件。C#驱动的基本数据库连接,增删改查操作。在使用C#驱动的时候,要在工程中添加"MongoDB.Bson.dll"和"MongoDB.Driver.dll"的引用。同时要在代…

如何在Windows 10的地图应用程序中获取离线地图

If you know you’re going to be using your PC in a location without an Internet connection, and you need access to maps, you can download maps for specific areas in the “Maps” app in Windows 10 and use them offline. 如果您知道要在没有Internet连接的地方使…

Hive初识(二)

Hive分区Hive组织表到分区。它是将一个表到基于分区列,如日期,城市和部门的值相关方式。使用分区,很容易对数据进行部分查询。表或分区是细分成桶,以提供额外的结构,可以使用更高效的查询的数据。桶的工作是基于表的一…

网站计数器 web映射

站点的网站计数器的操作 <% page import"java.math.BigInteger" %> <% page import"java.io.File" %> <% page import"java.util.Scanner" %> <% page import"java.io.FileInputStream" %> <% page import…

XenApp_XenDesktop_7.6实战篇之八:申请及导入许可证

1. 申请许可证 Citrix XenApp_XenDesktop7.6和XenServer 6.5申请许可证的步骤是一致的&#xff0c;由于之前我已经申请过XenApp_XenDesktop的许可证&#xff0c;本次以XenServer6.5的许可证申请为例。 1.1 在申请试用或购买Citrix产品时&#xff0c;收到相应的邮件&#xff0…