CAS引发的ABA问题

何为ABA问题?

        我们在使用CAS时,当两个线程要修改同一个值,第一个修改初始数值为100,而第二个线程修改初始数值100为101,然后又将101修改为100。如果第二个进行了100->101->100这个过程的修改后,第一个线程再修改,此时就出现了ABA问题,第一个线程修改的100不是原来的100,而是被第二个线程修改过的100。

static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);

public static void main(String[] args) {

    //以下是ABA问题的产生
    new Thread(() -> {
       atomicReference.compareAndSet(100,101);
       atomicReference.compareAndSet(101,100);

    },"t1").start();

    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(1);//第二个线程先等一秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(atomicReference.compareAndSet(100, 2021) + "\t" + atomicReference.get());

    },"t2").start();

    try {
        TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

运行结果:

 我们发现它成功修改为2021,而此刻的100时被第一个线程修改过的,不是原来的那个值,这就是ABA问题。

那么我们该如何解决这个问题呢?

我们可以加个版本号,这样就可以通过版本号来判断它是否被修改过。

示例代码:

 static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);

  public static void main(String[] args) {
    //以下是ABA问题的解决
    System.out.println("==========================以下是ABA问题的解决==========================");

    new Thread(() -> {
        //获取初始版本号
        int stamp = atomicStampedReference.getStamp();
        System.out.println(Thread.currentThread().getName() + "\t第一次版本号:" + stamp );


        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //修改原来的值,然后再修改为原来的值
        atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
        System.out.println(Thread.currentThread().getName() + "\t第二次版本号:" + atomicStampedReference.getStamp());
        atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
        System.out.println(Thread.currentThread().getName() + "\t第三次版本号:" + atomicStampedReference.getStamp());
    },"t3").start();

    new Thread(() -> {
        //获取初始版本号
        int stamp = atomicStampedReference.getStamp();
        System.out.println(Thread.currentThread().getName() + "\t第一次版本号:" + stamp );

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "\t第二次版本号:" + atomicStampedReference.getStamp() );
        //比较并交换,若版本号不是初始版本号,则返回false,并且不会修改该对象的值
        boolean result = atomicStampedReference.compareAndSet(100,2021,stamp,stamp+1);
        System.out.println("是否修改成功:" + result);

    },"t4").start();
}

运行结果:

总结:

        CAS会引发ABA问题,在工作中,如果应用环境允许出现ABA问题,我们可以忽略这个问题不去加版本号,用AtomicReference即可,若无法忽略,则使用AtomicStampedReference。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鲤鱼程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值