【Java基础】CAS (Compare And Swap) 操作

简介: CAS其实就是Compare And Swap的一个缩写,顾名思义就是比较并交换,其实就是把当前值与你预期的值进行一个比较,是一种用于在多线程环境下实现同步功能的机制。
关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

一、导读

我们继续总结学习Java基础知识,温故知新。

二、概览

CAS其实就是Compare And Swap的一个缩写,顾名思义就是比较并交换,其实就是把当前值与你预期的值进行一个比较,是一种用于在多线程环境下实现同步功能的机制。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。
在这里插入图片描述

CAS是原子性的操作(读和写两者同时具有原子性),是让CPU比较内存中某个值是否和预期的值相同,如果相同则将这个值更新为新值,不相同则不做更新.
对比交换是一条CPU的原子指令,其实现方式是基于硬件平台的汇编指令

CAS操作是原子性的,所以多线程并发使用CAS更新数据时,可以不使用锁。

使用volatile关键字可以保证可见性和有序性,但是却没有原子性,比如a++,引入cas就可以解决多线程的原子性问题。

CAS配合volatile来实现许多高并发类
在这里插入图片描述

三、使用场景

Java中利用CAS的乐观锁、原子性的特性高效解决了多线程的安全性问题。

CAS这种方式适用于并发量不高的情况。

四、原理

实现方式是通过借助C/C++调用CPU指令完成的,是一条CPU的原子指令,依赖于系统。

CAS的实现主要在JUC中的atomic包,存放在 java.util.concurrent.atomic 类路径下
如:自增长 AtomicInteger 等

Java中的CAS操作的执行依赖于Unsafe类,我们看下AtomicInteger的代码:

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    
    private volatile int value;
      
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    
}

CAS配合volatile来实现多线程可见性有序性和原子性。

再看下Unsafe类

    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

我们进入Unsafe类的native方法compareAndSwapInt,调用UnSafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令,这是一种完全依赖于硬件的功能,通过它实现了原子操作。

由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS操作是一条CPU的原子指令,不会造成所谓的数据不一致的问题,所以说CAS是线程安全的。

假如一个线程操作数据,干了一半活,累了,想要去休息。(貌似今天的线程体质都不太好)。于是它记录下当前数据的状态(就是数据的值),回家睡觉了。 醒来后打算继续接着干活,但是又担心数据可能被修改了,于是就把睡觉前保存的数据状态拿出来和现在的数据状态比较一下,如果一样,说明自己在睡觉期间,数据没有被人动过(当然也有可能是先被改成了其它,然后又改回来了,这就是ABA问题了),那就接着继续干。如果不一样,说明数据已经被修改了,那之前做的那些操作其实都白瞎了,就干脆放弃,从头再重新开始处理一遍。

所以CAS这种方式适用于并发量不高的情况,也就是数据被意外修改的可能性较小的情况。如果并发量很高的话,你的数据一定会被修改,每次都要放弃,然后从头再来,这样反而花费的代价更大了,还不如直接加锁呢。

五、优劣

5.1 缺点:

  1. 循环时间长开销大(CAS自旋操作):当内存地址V与预期值B不相等时会一直循环比较,直到相等
  2. 只能保证一个共享变量的原子操作,不能保证代码块的原子性。
  3. 存在ABA问题
并发1(上):获取出数据的初始值是A,后续计划实施CAS乐观锁,期望数据仍是A的时候,修改才能成功
并发2:将数据修改成B
并发3:将数据修改回A
并发1(下):CAS乐观锁,检测发现初始值还是A,进行数据修改
并发1在修改数据时,虽然还是A,但已经不是初始条件的A了,
中间发生了A变B,B又变A的变化,此A已经非彼A,数据却成功修改,
可能导致错误,这就是CAS引发的所谓的ABA问题。
优化ABA问题:
    “版本号”的比对,一个数据一个版本,版本变化,即使值相同,也不应该修改成功。
    很好解决,再加一个版本号字段就行了,并规定只要修改数据,必须使版本号加1。

5.2 优点

  • 没有引用锁的概念,并发量不高情况下提高效率
  • 减少线程上下文切换
相关文章
|
15天前
|
安全 Java 调度
解锁Java并发编程高阶技能:深入剖析无锁CAS机制、揭秘魔法类Unsafe、精通原子包Atomic,打造高效并发应用
【8月更文挑战第4天】在Java并发编程中,无锁编程以高性能和低延迟应对高并发挑战。核心在于无锁CAS(Compare-And-Swap)机制,它基于硬件支持,确保原子性更新;Unsafe类提供底层内存操作,实现CAS;原子包java.util.concurrent.atomic封装了CAS操作,简化并发编程。通过`AtomicInteger`示例,展现了线程安全的自增操作,突显了这些技术在构建高效并发程序中的关键作用。
41 1
|
1月前
|
Java BI 数据处理
如何在Java中实现Excel操作
如何在Java中实现Excel操作
|
2月前
|
存储 Java
java用modbus4j的RTU去操作那些寄存器(线圈,保持,输入,离散输入寄存器)
java用modbus4j的RTU去操作那些寄存器(线圈,保持,输入,离散输入寄存器)
55 0
|
2月前
|
Java 数据库 数据安全/隐私保护
Java操作Excel文件导入导出【内含有 jxl.jar 】
Java操作Excel文件导入导出【内含有 jxl.jar 】
47 0
|
21天前
|
存储 Java 索引
Java ArrayList操作指南:如何移除并返回第一个元素
通过上述方法,你可以方便地从Java的 `ArrayList` 中移除并返回第一个元素。这种操作在日常编程中非常常见,是处理列表时的基本技能之一。希望这篇指南能帮助你更好地理解和运用Java的 `ArrayList`。
24 4
|
20天前
|
安全 Oracle Java
(四)深入理解Java并发编程之无锁CAS机制、魔法类Unsafe、原子包Atomic
其实在我们上一篇文章阐述Java并发编程中synchronized关键字原理的时候我们曾多次谈到过CAS这个概念,那么它究竟是什么?
|
1月前
|
分布式计算 DataWorks Java
DataWorks操作报错合集之使用ODPS Tunnel Upload功能时,遇到报错:Java 堆内存不足,该如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
1月前
|
SQL 缓存 Java
使用MyBatis优化Java持久层操作
使用MyBatis优化Java持久层操作
|
1月前
|
Java API 开发者
Java中的文件I/O操作详解
Java中的文件I/O操作详解
|
1月前
|
Java BI 数据处理
如何在Java中实现Excel操作
如何在Java中实现Excel操作