简介

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. 出现 error: RPC failed; curl 18 transfer closed with outstanding read data remaining 的原因

    最近在做全栈项目,前台后台,服务器端,三端在一个文件夹,当git clone 项目的时候就会出现:error: RPC failed; curl 18 transfer closed with out ...

  2. [HNOI2009]双递增序列

    不难发现本题贪心是不好做的,可以考虑 \(dp\). 首先的一个想法就是令 \(dp_{i, j, k, l}\) 表示当前选到第 \(i\) 个位置,当前第一个序列选了 \(j\) 个数,当前第一个 ...

  3. DESUtil

    package com.tebon.ams.util;import sun.misc.BASE64Decoder;import javax.crypto.Cipher;import javax.cry ...

  4. JAVA之容器(转)

    一.概览 容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表. Collection 1. Set TreeSe ...

  5. Mysql Json函数之搜索 (三)

    本节中的函数对JSON值执行搜索操作,以从其中提取数据,报告数据是否在其中的某个位置或报告其中的数据的路径. JSON_CONTAINS(target, candidate[, path]) 通过返回 ...

  6. java在访问https资源时,忽略证书信任问题 (转)

    java程序在访问https资源时,出现报错sun.security.validator.ValidatorException: PKIX path building failed: sun.secu ...

  7. 一文详解Kafka API

    摘要:Kafka的API有Producer API,Consumer API还有自定义Interceptor (自定义拦截器),以及处理的流使用的Streams API和构建连接器的Kafka Con ...

  8. VUE3 之 动态组件 - 这个系列的教程通俗易懂,适合新手

    1. 概述 暗示效应告诉我们: 巧妙的暗示会在不知不觉中剥夺我们的判断力,对我们的思维形成一定的影响,造成我们行为的些许改变或者偏差. 例如你的朋友说你脸色不太好,是不是病了,此时,你可能就会感觉浑身 ...

  9. 任意文件上传漏洞syr

    任意文件上传漏洞 先来几个一句话木马看看 <%execute(request("value"))%> #asp木马 <?php eval($_POST[" ...

  10. 带你十天轻松搞定 Go 微服务系列(九、链路追踪)

    序言 我们通过一个系列文章跟大家详细展示一个 go-zero 微服务示例,整个系列分十篇文章,目录结构如下: 环境搭建 服务拆分 用户服务 产品服务 订单服务 支付服务 RPC 服务 Auth 验证 ...