CAS缺点,以及ABA问题和解决办法(AtomicStampedReference)

本文探讨了Java中CAS(Compare and Set)的缺点,如循环等待开销大,并解释了ABA问题。通过实例说明如何利用AtomicStampedReference解决ABA问题,通过版本号控制确保操作原子性。
摘要由CSDN通过智能技术生成

CAS缺点,以及ABA问题和解决办法



java中多线程之CAS(compareAndSet),Unsafe类大白话详解.

CAS缺点

  1. 循环时间长,开销大
    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

如果this.compareAndSwapInt(var1, var2, var5, var5 + var4)期望值判断一直为False(当前线程比较倒霉,总有其他线程在我前面进行过操作),就会一直循环,给CPU带来很大的开销。

  1. 只能保证一个共享变量的原子操作
  2. ABA问题
    什么是ABA问题。
    看一个流程案例:
    1. 现在有1、2两个线程进来操作同一个变量A
    2. 1线程跑得快,把A改成了B
    3. 1线程又把B改回了A
    4. 2线程来通过CAS想把A改成B,发现内存中的值是A,符合自己的期望值,以为中途没有线程对它进行更改,就把值改为了B

整个过程虽然操作成功,但不代表过程没有问题!

如何解决ABA问题呢?

新建一个User类

class User{
    String name;

    public User(String name) {
        this.name = name;
    }
}

测试:通过AtomicReference创建我们自定义的User类的原子引用。

    public static void main(String[] args) {
        User user1 = new User("张三");
        User user2 = new User("李四");

        AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(user1);

        System.out.println(atomicReference.compareAndSet(user1, user2));
        System.out.println(atomicReference.get().name);
    }

在这里插入图片描述

上面的例子很简单哈,通过cas将原子类的引用从张三改为李四。

先不管上面的代码,现在我们提出一个办法来解决ABA问题:修改版本号(时间戳),类似乐观锁

重写我们的测试代码:用上AtomicStampedReference。使用AtomicStampedReference中的compareAndSet(var1,var2,var3,var4)方法
var1:期望值
var2:修改值
var3:期望版本号
var4:修改版本号

public class CasDemo {

        static User user1 = new User("张三");
        static AtomicReference<User> atomicReference = new AtomicReference<>(user1);
        //初始化版本号为1,变量为张三
        static AtomicStampedReference<User> atomicStampedReference = new AtomicStampedReference<>(user1,1);
    public static void main(String[] args) {
        new Thread(()->{
            User user2 = new User("李四");
            //获取版本号
            int num = atomicStampedReference.getStamp();
            System.out.println("线程1第一次获取到版本号"+num);
            //ABA操作
            atomicStampedReference.compareAndSet(user1,user2,1,2);
            atomicStampedReference.compareAndSet(user2,user1,2,3);
        },"线程1").start();

        new Thread(()->{
            //获取版本号
            int num = atomicStampedReference.getStamp();
            System.out.println("线程2第一次获取到版本号"+num);
            //暂停一秒,保证线程1完成了一次ABA操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            User user2 = new User("李四");
            atomicStampedReference.compareAndSet(user1,user2,1,2);
            System.out.println(atomicReference.get().name);
        },"线程2").start();
    }
}

1. 线程1,开始ABA操作,版本化递增到了3。
2. 线程2,开始ABA操作,由于版本化期望值对比已经改变,修改无效,解决了ABA问题

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

易柏州Innovation

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

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

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

打赏作者

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

抵扣说明:

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

余额充值