简介

CAS 的全称为 Compare-And-Swap,他是一条 CPU 并发源语。

他的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。

CAS 并发原语体现在 JAVA 语言中就是 sun.misc.Unsafe 类中的各个方法。调用 UnSafe 类中的 CAS 方法,JVM 会帮我们实现出 CAS 汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题

代码演示

public class CASDemo {

    public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5); System.out.println(atomicInteger.compareAndSet(5, 2022) + "=====" + atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5, 2014) + "=====" + atomicInteger.get());
} }

原理讲解

CAS 就是基于 unsafe 类去实现的,底层汇编。

源码解析

截取了 AtomicInteger 类中的部分代码

    private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value; /**
* Atomically increments by one the current value.
* this:当前对象
* valueOffset:内存偏移量
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}

Unsafe 中的部分源代码

 public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}

Unsafe

是 CAS 的核心类,由于 Java 方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe 相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe 内存在于 sun.mics 包中,其内部方法操作可以像C的指针一样直接操作内存,因为 Java 中 CAS 操作得知执行依赖于 Unsafe 类的方法。

注意 Unsafe 类中所有的方法都是 native 修饰的,也就是说 Unsafe 类中的方法都是直接调用操作系统底层资源执行相应任务。

valueOffset 属性

表示该变量在内存中的偏移地址,因为 Unsafe 就是根据内存偏移地址获取数据的。

/**
* Atomically increments by one the current value.
* this:当前对象
* valueOffset:内存偏移量
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}

使用 volatile 修饰保证了可见性,一旦修改其他类可以看见。

private volatile int value;

代码执行步骤讲解

假设线程A和线程B两个线程同时执行getAndAddlnt操作(分别跑在不同CPU上):

  1. AtomicInteger 里面的value原始值为5,即主内存中 AtomicInteger 的 value 为 5,根据JMM 模型,线程 A 和线程 B 各自持有一份值为 5 的 value 的副本分别到各自的工作内存。
  2. 线程 A 通过 getlntVolatile(var1,var2)拿到value值5,这时线程A被挂起。
  3. 线程 B 也通过 getlntVolatile(var1,var2)方法获取到value值5,此时刚好线程 B 没有被挂起并执行 compareAndSwaplnt 方法比较内存值也为 5,成功修改内存值为6,线程B打完收工,一切OK。
  4. 这时线程 A 恢复,执行 compareAndSwaplnt 方法比较,发现自己手里的值数字 5 和主内存的值数字 6 不一致,说明该值己经被其它线程抢先一步修改过了,那A线程本次修改失败,只能重新读取重新来一遍了
  5. 线程 A 重新获取 value 值,因为变量 value 被 volatile 修饰,所以其它线程对它的修改,线程 A 总是能够看到,线程 A 继续执行 compareAndSwaplnt 进行比较替换,直到成功。

CAS 比较并交换的更多相关文章

  1. 原子类型的使用&Unsafe&CAS

    在项目中也经常可以见到原子类型(AtomicXXX)的使用,而且AtomicXXX常用来代替基本类型或者基本类型的包装类型,因为其可以在不加同步锁的情况下保证线程安全(只对于原子操作). 下面以Ato ...

  2. DLC双端锁,CAS,ABA问题

    一.什么是DLC双端锁?有什么用处? 为了解决在多线程模式下,高并发的环境中,唯一确保单例模式只能生成一个实例 多线程环境中,单例模式会因为指令重排和线程竞争的原因会出现多个对象 public cla ...

  3. CAS 分析

    CAS是什么 (1) CAS(Compare and Swap) 比较并交换, 比较并交换是在多线程并发时用到的一种技术 (2) CAS是原子操作, 保证并发安全性, 而不是保证并发同步. (3) C ...

  4. CAS原理解析

    CAS底层原理 概念 CAS的全称是Compare-And-Swap,它是CPU并发原语 它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的 CAS并发原语体现在Jav ...

  5. 基础篇:详解锁原理,volatile+cas、synchronized的底层实现

    目录 1 锁的分类 2 synchronized底层原理 3 Object的wait和notify方法原理 4 jvm对synchronized的优化 5 CAS的底层原理 6 CAS同步操作的问题 ...

  6. ReentrantLock锁-CAS与阻塞

    ReentrantLock锁 ReentrantLock通过原子操作和阻塞实现锁原理,一般使用lock获取锁,unlock释放锁 lock的时候可能被其他线程获得所,那么此线程会阻塞自己,关键原理底层 ...

  7. CAS你知道吗?底层如何实现?ABA问题又是什么?关于这些你知道答案吗

    CAS你知道吗?如何实现? 1. compareAndSet 在volatile当中我们提到,volatile不能保证原子语义,所以当用到变量自增时,如果用到synchronized会太"重 ...

  8. ConcurrentLinkedQueue代码解析

    原因:学习ConcurrentLinkedQueue是看到akka框架的默认邮箱是使用ConcurrentLinkedQueue实现的. 1. ConcurrentLinkedQueue在java.u ...

  9. java并发编程(8)原子变量和非阻塞的同步机制

    原子变量和非阻塞的同步机制 一.锁的劣势 1.在多线程下:锁的挂起和恢复等过程存在着很大的开销(及时现代的jvm会判断何时使用挂起,何时自旋等待) 2.volatile:轻量级别的同步机制,但是不能用 ...

随机推荐

  1. elasticsearch查询之大数据集分页查询

    一. 要解决的问题 search命中的记录特别多,使用from+size分页,直接触发了elasticsearch的max_result_window的最大值: { "error" ...

  2. git reset HEAD 与 git reset --hard HEAD的区别

    感谢原文作者:天地逍遥 原文链接:https://www.jianshu.com/p/aeb50b94e6c0 git reset HEAD 是将咱暂存区和HEAD的提交保持一致 git reset ...

  3. UIView属性的讲解

    1.父控件和子控件的理解在storyboard中只有UIView是可以在里面拖入子控件的,其他控件不可以(必须通过代码添加)拖入一个UIView控件,在里面添加一些子控件(UIView控件是控制器的V ...

  4. 控制器view生命周期

    控制器View的生命周期方法:只要是控制器的生命周期方法,都是以view开头. 控制器View加载完成时调用- (void)viewDidLoad { [super viewDidLoad];} 控制 ...

  5. mysql启动错误:mysql.sock丢失

    ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2) 我的是Cent ...

  6. Web集群调度器-Haproxy

    Web集群调度器-Haproxy 目录 Web集群调度器-Haproxy 一.Web集群调度器 1.常用的Web集群调度器 2. Haproxy应用分析 3. Haproxy的主要特性 4. 常用集群 ...

  7. 在MacOS安装puppeteer

    安装node:升级:npm i npm 安装yarn:需要注意先把yarn的流程跑完,特别是package.json 安装puppeteer:yarn add puppeteer 安装完成以后需要重启 ...

  8. Spring Druid多数据源配置

    SpringBoot 多数据源配置 如果需要在一个应用中使用多个数据源,应当如何实现呢,在Spring配置MyBatis中,我们可以看到以下的代码 <!-- mybatis 的SqlSessio ...

  9. NeurIPS 2017 | TernGrad: Ternary Gradients to Reduce Communication in Distributed Deep Learning

    在深度神经网络的分布式训练中,梯度和参数同步时的网络开销是一个瓶颈.本文提出了一个名为TernGrad梯度量化的方法,通过将梯度三值化为\({-1, 0, 1}\)来减少通信量.此外,本文还使用逐层三 ...

  10. pytest(12)-Allure常用特性allure.attach、allure.step、fixture、environment、categories

    上一篇文章pytest Allure生成测试报告我们学习了Allure中的一些特性,接下来继续学习其他常用的特性. allure.attach allure.attach用于在测试报告中添加附件,补充 ...