CAS原理分析


CAS的全称为Compare And Swap,是一条CPU并发源语,核心为Unsafe类,用于直接操作底层内存中的值,属于原子操作( 依赖硬件)。

1、CAS实现原理?

CAS底层为Unsafe类,所有方法都是原子操作,根据其Unsafe的并发原语原理即系统原语,原语是系统操作层面,若干条
指令组成,原语的操作是连续的,不可中断的,就和数据库中的原子性同理。如:

    /**
    * 对指定对象var1进行特定值增加var4
    * var2是原var1的地址偏移量
    * 循环使用cas操作进行比较替换
    */
  public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);//根据var1对象和var2的地址偏移量获取var1对象值
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//cas操作替换
    return var5;//返回旧的值
  }

2、CAS适用场景和缺点?

适用场景

(1)CAS因其特性,使用循环对比替换操作,在线程并发较少情况下,自旋比较次数少,减少cpu消耗;
(2)synchronized在jdk1.6进行优化即锁粗化、锁消除、轻量级锁、偏向锁、自旋等后,并发较高情况下,性能比CAS强。

CAS缺点

(1)ABA问题;
多线程并发cas操作,内存值和预期值都是A,期间发生了A->B->A的更新,仅仅判断数值是A,可能导致不合理的修改操作。
针对这种情况,Java提供了AtomicStampedReference工具类,通过为引用建立类似版本号(stamp)的方式,来保证CAS的正确性。
解决ABA问题:

int temp = 20;//初始值
AtomicStampedReference<Integer> stamped = new AtomicStampedReference<>(temp,1);//初始化引用类型的值和版本号
int stamp = stamped.getStamp()+1;//stamp版本号即每个值对应一个Stamp值
int newTemp = 25;//新值
stamped.compareAndSet(temp,newTemp,1,stamp);//根据版本号和预期值,设置新的值和新的版本号
System.out.println(stamped.getReference());//上述根据版本号执行成功时会输出25,否则是20

(2)循环重试机制导致内存开销大;
CAS中使用的失败重试机制即自旋(循环对比),每次只会保证一个线程能执行成功,利用预期值来保证替换操作。
虽然CAS是乐观锁,但是总有意外情况,所以在有需要的时候,还是要考虑限制自旋的次数,以免过度消耗CPU。
(3)只能保证一个共享变量的原子操作;

3、如何实现乐观锁?

核心思想:以AtomicInteger原子操作为核心;
第一种实现方式:
(1)利用Unsafe类去保证原子性,因为其类中的方法都是原子操作,cas本来就是一种乐观锁;
(2)定义数据集:定义一个变量用于比较交换,实例化一个Unsafe类对象,用于调用其原子方法;
第二种实现方式:自定义实现原子操作且不加锁(思想如下);
(1)定义数据集包括:原子变量、自定义比较交换逻辑;

private static final Unsafe unsafe = Unsafe.getUnsafe();//unsafe类对象
private static final long valueOffset;//底层交换变量地址偏移量
private volatile int value;//比较交换的变量(根据实际情况定义类型)
static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

(2)避免加锁,可以在交换逻辑中使用while循环,保证并发场景实现数据一致性。

public final int getAndSetInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);//根据var1对象和var2的地址偏移量获取var1对象值
    } while(!this.compareAndSwapInt(var1, var2, var5, var4));//cas操作替换
    return var5;//返回旧的值
}

4、AtomicInteger实现原理?

实现原理

根据自旋和非自旋CAS调用Unsafe类中的原子操作方法。
(1)首先定义存储值value、地址偏移量和unsafe类对象变量;

private volatile int value;
private static final long valueOffset;
private static final Unsafe unsafe;

(2)自身所有方法都是基于Unsafe类中的原子操作方法,使用unsafe的objectFieldOffset去获取value值的地址偏移量;

valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));//获取地址偏移量

(3)Unsafe类中原子方法分为本地native的CAS和自旋式CAS;

//本地native的CAS:
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);//调用本地方法(C++底层方法)

//自旋式CAS:
public final int getAndSetInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);//根据var1对象和var2的地址偏移量获取var1对象值
    } while(!this.compareAndSwapInt(var1, var2, var5, var4));//cas操作替换
    return var5;//返回旧的值
}

AtomicInteger实现源码

AtomicInteger类:

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;//偏移量
    private volatile int value;//存储值
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));//获取地址偏移量
        } catch (Exception ex) { throw new Error(ex); }
    }
    /**
    * 只列举两个方法分析,还有自增、指定值操作加减等更多方法
    */
    public final int getAndSet(int newValue) {//直接设置值
        return unsafe.getAndSetInt(this, valueOffset, newValue);//直接对value设置newValue值
    }

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);//基于期望值对比设置新的update值
    }
}

Unsafe类:

public final int getAndSetInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);//根据var1对象和var2的地址偏移量获取var1对象值
    } while(!this.compareAndSwapInt(var1, var2, var5, var4));//cas操作替换
    return var5;//返回旧的值
}

//调用本地方法(C++底层方法)
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值