Java并发编程之CAS简析与ABA问题的解决方案

什么是CAS

CAS全称Compare And Swap,即比较并交换。CAS的功能是,先判断内存中的目标变量与给定期望值是否相同,如果相同,则为目标变量赋一个新值。CAS是乐观锁的一种实现,在锁竞争不大的情况下使用CAS实现同步的效率比悲观锁(synchronized等3)同步实现高,在Java并发包中具有广泛应用。

Java中CAS的并发原语全部体现在sum.misc.Unsafe类中,Unsafe类中的方法均为native方法,在相对应的C语言的函数实现中,最终在C函数中调用汇编指令实现CAS操作。总结来说就是CAS实现是基于汇编指令的,也就意味着CAS是一种依赖于硬件的操作,他的原子性与线程安全性完全由硬件保障(Cpu原子指令)。

ABA问题

所谓ABA问题就是,当有多个线程对同一个原子类进行操作时,在某一线程内原子类的值先由A被修改成B,再由B修改回A ,此时这两个修改操作其他线程是无法察觉的。

简单示例说明如下:

public class Test {

    public static void main(String[] args) {
        AtomicInteger atomicInt = new AtomicInteger(1);
        atomicInt.compareAndSet(1, 2);
        atomicInt.compareAndSet(2, 1);
        System.out.println(atomicInt.get());
        boolean result = atomicInt.compareAndSet(1, 2);
        System.out.println(result);
    }
}

输出结果如下:

1
true

解决方案

ABA问题出现的根本原因是CAS只关注值的最终结果,而不关注值修改的过程,只需要对修改过程也关注并记录就可以解决问题,Java中实际上是添加了版本号来解决ABA问题,具体体现在Java并发包下的原子类AtomicStampedReference。

AtomicStampedReference内用静态内部类Pair的示例存储数据,静态内部类Pair定义了两个字段,一个用于存放引用,另一个则是版本号,结构如下:

private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

    private volatile Pair<V> pair;

示例如下:

public class Test {

    public static void main(String[] args) {

        AtomicInteger v1 = new AtomicInteger(1);
        AtomicInteger v2 = new AtomicInteger(2);
        AtomicStampedReference<AtomicInteger> reference = new AtomicStampedReference<AtomicInteger>(v1, 0);
        // 记录原始版本
        int stamp = reference.getStamp();
        // 模拟其它线程ABA修改变量
        reference.compareAndSet(v1, v2, reference.getStamp(),
                reference.getStamp() + 1);
        reference.compareAndSet(v2, v1, reference.getStamp(),
                reference.getStamp() + 1);
        System.out.println(reference.getReference());
        
        boolean result = reference.compareAndSet(v1, v2, stamp, stamp + 1);
        System.out.println(result);
    }
}

输出结果如下

1
false

CAS的其他缺点

  • 只能保证一个变量的原子性,无法保证整个代码块的原子性。
  • 自旋带来的Cpu开销大,在高竞争环境下使用CAS修改某变量,修改失败时只能进入死循环自旋继续尝试修改,浪费Cpu资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

7rulyL1ar

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

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

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

打赏作者

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

抵扣说明:

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

余额充值