简介

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. C# 实例解释面向对象编程中的单一功能原则

    在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...

  2. 阿里P8整理Mysql面试题答案,助你“脱颖而出”,吊打面试官!(建议收藏)

    前言 作为一名开发人员,每天英高都在和数据库进行着斗智斗勇,尤其是互联网行业,对MySQL的使用是比较多的.同样的,因为mysql的重要性以及普及性,在面试的时候一定是一个面试的重点或者说常问问题,说 ...

  3. shiro-springboot整合,平行依赖

    最近在弄shiro,把shiro单独提出来,弄成一个子项目,这样可以降低项目的冗余,但是把shiro打成jar包后,另一个项目添加其依赖后,在IDE项目中正常的运行,打成jar包后运行,则不断的报错 ...

  4. 入门-Kubernetes概述 (一)

    1 Kubernetes是什么 Kubernetes是Google在2014年开源的一个容器集群管理系统,Kubernetes简称K8S. K8S用于容器化应用程序的部署,扩展和管理. K8S提供了容 ...

  5. EasyExcel小试牛刀

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/12029411.html 某种偶然的机会遇到了这个插件, 听说很牛X, 我之前也不知道, 不过还 ...

  6. C语言中的单引号和双引号的区别

    首先肯定地说,二者是有区别的,不是说用谁都一样. 1.实质区别,代表的含义不同 'A'代表的是一个整数,而且这个整数对应的是编译器所采用的字符集中的字符序列对应的数值.所以'A'跟ASCII中的65意 ...

  7. MySQL--数据表操作--行转列和列转行

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11782549.html MySQL--数据表操作: 会用到的查询操作. 1. Limit的用法 ...

  8. js实现网页回弹小球效果

    直接上效果图 运行页面会首先弹出一个输入框,询问用户想要产生的小球数量,随后后台就会产生指定数量的小球,在页面中来回跳动,触碰到页面边框时,就会回弹,且产生的小球颜色随机,小球在页面中的位置随机,小球 ...

  9. Ubuntu - root, sudo, su, passwd

    1.rootubuntu中默认是不使用root账户的,当然也是可以开启并设置为默认登录账户的,但ubuntu不建议使用而已,毕竟root账户拥有所有权限,可能会出现一些误操作之类.在普通账户中,如果遇 ...

  10. 使用VMware安装win10虚拟机

    (1)打开VMware: (2)打开左上角的文件,点击新建虚拟机: (3)选择典型,下一步: (4)选择稍后安装操作系统,下一步: (5)选择win10×64,下一步: (6)可随意修改虚拟机名称,位 ...