【Java基础】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 优点

  • 没有引用锁的概念,并发量不高情况下提高效率
  • 减少线程上下文切换

六、 推荐阅读

【Java基础】原子性、可见性、有序性

【Java基础】java可见性之 Happens-before

【Java基础】java-android面试Synchronized

【Java基础】java-android面试-线程状态

【Java基础】线程相关

【Java基础】java 异常

【Java基础】java 反射

【Java基础】java 泛型

【Java基础】java注解

【Java基础】java动态代理

【Java基础】Java SPI

【Java基础】Java SPI 二 之 Java APT

【Java基础】 jvm 堆、栈、方法区 & java 内存模型

【Java基础】volatile关键字

【Java基础】CountDownLatch

猜你喜欢

转载自blog.csdn.net/fumeidonga/article/details/131542090