什么是CAS

CAS即Compare 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. 如果值更新成功则返回true给Unsafe.java,没有更新则返回false

CPU在这里发挥的作用:

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

CAS的缺点

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

ABA问题

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

  • 线程1,期望值为A,欲更新的值为B
  • 线程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极大的开销。

这种循环也称为自旋

解决方法:

  1. 限制自旋次数,防止进入死循环。
  2. 只能保证一个共享变量的原子操作
  3. CAS的原子操作只能针对一个共享变量。

解决方法:

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

CAS的应用

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

乐观锁

乐观锁总是假设最好的情况,每次去操作数据都认为不会被别的线程修改数据,所以在每次操作数据的时候都不会给数据加锁,即在线程对数据进行操作的时候,别的线程不会阻塞仍然可以对数据进行操作,只有在需要更新数据的时候才会去判断数据是否被别的线程修改过,如果数据被修改过则会拒绝操作并且返回错误信息给用户。

悲观锁

悲观锁总是假设最坏的情况,每次去操作数据时候都认为会被的线程修改数据,所以在每次操作数据的时候都会给数据加锁,让别的线程无法操作这个数据,别的线程会一直阻塞直到获取到这个数据的锁。这样的话就会影响效率,比如当有个线程发生一个很耗时的操作的时候,别的线程只是想获取这个数据的值而已都要等待很久。

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


更多关于CAS的原理分析详细讲解可以到:https://www.bilibili.com/video/BV1bg411F7U9/观看

如果你觉得有所收获,可以点赞收藏关注哦,接下来,我将继续对Android的相关知识进行分析和分享,可以继续关注哦,要是真正想学习android开发这块的,或者在这方面工作的朋友我免费分享给你,当然也希望大家都能多多支持我,你们的点赞就是我的动力,谢谢!

一文搞懂CAS,CAS原理分析及ABA问题详解相关推荐

  1. CAS原理分析及ABA问题详解

    什么是CAS? CAS(Compare-and-Swap,比较并替换),CAS需要有3个操作数: 内存地址V,旧的预期值A,新值B; 其作用是当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修 ...

  2. 【 卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10)】

    卷积神经网络CNN 数学原理分析与源码详解 深度学习 Pytorch笔记 B站刘二大人(9/10) 本章主要进行卷积神经网络的相关数学原理和pytorch的对应模块进行推导分析 代码也是通过demo实 ...

  3. 一文搞懂线程池原理——Executor框架详解

    文章目录 1 使用线程池的好处 2 Executor 框架 2.1 Executor 框架结构 2.2 Executor 框架使用示意图 2.3 Executor 框架成员 2.3.1 Executo ...

  4. 搞懂 SQL 查询优化原理分析,秒速处理大数据量查询

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 有一张财务流水表,未分库分表,目前的数据量为9555695,分页查询使用到 ...

  5. 一文读懂——全局注意力机制(global attention)详解与代码实现

    废话不多说,直接先上全局注意力机制的模型结构图. 如何通过Global Attention获得每个单词的上下文向量,从而获得子句向量呢?如下几步: 代码如下所示: x = Embedding(inpu ...

  6. 【多输入模型 Multiple-Dimension 数学原理分析以及源码详解 深度学习 Pytorch笔记 B站刘二大人 (6/10)】

    多输入模型 Multiple-Dimension 数学原理分析以及源码源码详解 深度学习 Pytorch笔记 B站刘二大人(6/10) 数学推导 在之前实现的模型普遍都是单输入单输出模型,显然,在现实 ...

  7. word2vec原理分析与代码公式详解

    word2vec 是 Google 于 2013 年开源推出的一个用于获取 word vector 的工具包,原始的论文和网上的很多分析都写的太过粗略,难以理解.现在有根据源码的具体原理分析和详细的公 ...

  8. 一文读懂汇编程序的多个分段的程序--详解

    先贴上程序 首先要知道cpu是不知道到底哪里的数据是代码 那里是数据那里是堆栈因此实现这些要靠程序员来告诉CPU 当然还有小小的汇编程序:汇编中的嵌套循环 assume cs:code,ds:data ...

  9. 一文搞懂H264量化原理以及计算过程

    1.概述 量化是使数据比特率下降的有效工具.量化过程的输入值动态范围很大,需要较多的比特才能表示一个数值,量化后的输出则只需要较小比特表示. 量化是不可逆过程,处理过程中有信息丢失,存在量化误差. H ...

最新文章

  1. 使用C# 3.0编译器编译 Asp.Net 项目代码
  2. 机翻降重?掩饰抄袭?SCI期刊上的这些「奇言怪语」,不少来自中国作者
  3. Hyper-V应用指南之6-差异磁盘的使用[转]
  4. Android实现手机手电筒
  5. Android开源项目整理:个性化空间View篇(看遍论坛千万篇,不看此篇也枉然)
  6. 50个常用元器件图片_盘点2020年wordpress常用的50个插件合集
  7. lambdas for_Java 8发布了! — Lambdas教程
  8. unique函数_unique函数使用场景(一)
  9. mysql有没有实现高可用_MySQL高可用架构:mysql+keepalived实现
  10. 关于vue中sync修饰符的用法
  11. Java String 类的方法
  12. js获取窗口宽度高度
  13. cf701B Cells Not Under Attack
  14. cpp的vector初始化方法
  15. 微信小程序获取openId,传参问题导致参数无效(errcode: 40013、errcode:40125、errcode“:40029)
  16. 空气净化器的研究概况
  17. 【《Linus Torvalds自传-Linux OS 之父》摘录】
  18. Java 核心技术卷 II(第 8 版) – 读书笔记 – 第 1 章(下)
  19. iPhone开发网站、论坛和博客
  20. 听歌学日语2 五十音图 たなは行

热门文章

  1. NGUI制作头顶血条个人小问题解决方法
  2. Nmap使用NSE进行漏洞扫描
  3. int类型变量的取值范围
  4. 基于kubeadm快速部署kubernetes K8S V1.17.4集群-无坑完整版
  5. *约瑟夫问题。这是十七世纪的法国数学家加斯帕在《数目的游戏问题》中讲的一个故事:15个基督教徒和15个异教徒在海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了个办法:30个人围成一个圆
  6. unity编辑器上不穿模,真机Android上会穿模问题定位和解决
  7. python加权求和_Python计算加权平均分的问题?大神求解答。。。。。
  8. 找工作,面试,这几个坑我希望你们不要踩
  9. 如何更高效的标注文字检测算法的label(二)
  10. 主机常见噪音 的原因 及 降噪方法