【JDK源码学习】JDK中的CAS操作及ABA问题如何解决

一、解释

CAS的全称是compare-and-swap,即比较和交换。虽然看起来的先比较再交换,无法保证原子性,其实其利用的是底层硬件,是一条CPU的原子指令,是线程安全的。jdk中的juc包中的线程安全性,都是基于CAS实现的。其处理过程是:CAS操作需要输出两个数值,其一是旧值(操作前的值),其二是新值,在操作期间先比较一下旧值是否发生的变化,如果没有变化,则用新值替换旧值,否则不交换。

二、源码解析

基于AtomicInteger类,来看看CAS的具体过程。

public class AtomicInteger extends Number implements java.io.Serializable {

    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); }
    }

    // volatile保存了value的可见性
    private volatile int value;

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }

    // 获取当前值
    public final int get() {
        return value;
    }

    // 设置新值
    public final void set(int newValue) {
        value = newValue;
    }

    // 设置新值并返回旧值
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

    // 比较交换
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    ...
}

可以看到AtomicInteger底层利用volatile和cas来进行更新数据。volatile修饰的value,保证了value更改之后,其他线程立马就能知道。Unsafe类提供的cas操作,保证了整个更新过程的原子性。

下面再看看Unsafe类的源码

public final class Unsafe {
    ...
    /**
     *
     * @param o 待更新的对象
     * @param offset 偏移量,不用管这个,c++底层会用到
     * @param newValue 新增
     * @return 先前的值
     * @since 1.8
     */
    public final int getAndSetInt(Object o, long offset, int newValue) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, newValue));
        return v;
    }
    ...
    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);
    ...
    
}

其中的方法compareAndSwapInt()是被natice修饰的本地方法,我们看不到方法的具体操作,其实它是由C++实现的本地方法,是一个原子性的比较交换操作。compareAndSwapInt()操作成功返回true,否则返回false。那么AtomicInteger是如何实现更新操作的呢,可以看getAndSetInt()方法。代码很简单,先取内存中的当前值v = getIntVolatile(o, offset),然后调用本地方法compareAndSwapInt去更新为新值newValue,如果失败,则重新获最当前值再去更新,这样一直循环下去,直到更新成功。

以上,就是AtomicInteger如何利用CAS来保证线程安全的。

三、缺点总结

1、ABA问题。CAS比较交换时,是检查当前值与期望值是否一致。试想一下,如果某个值由A变成了B,再由B变回了A,那么在做CAS比较时,会认为值没有变化,但实际是发生了变化。ABA问题的解决思路是给数据加一个版本号,每次更新后对其版本加1,这样在值变回A之后,其版本已不是原来的版本了。具体可参见jdk中的AtomicStampedReference

2、开销大。在高并发情况下,自旋CAS如果长时间不成功,会一直执行循环操作,给CPU带来非常大的执行开销。所以其适用于那些并发不是很大的场景。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值