CAS缺陷及其ABA问题解决

CAS的缺陷:

1、循环时间长,开销大。

因为do while循环,当CAS一直失败就会一直循环,直至成功,才会结束。
比如说线程2、3、4存心想针对反应有点慢的线程1,那么线程1这个小可怜只能是逆来顺受了。

2、只能保证一个共享变量的原子问题。

当只有一个共享变量时,我们可以用循环CAS的方法来保证其原子性,但是当有多个共享变量时,循环CAS就无法保证原子性,我们可以通过锁来保证原子性。

3、ABA问题---->狸猫换太子

简单说一下ABA问题。
ABA从这个名字上就可以看出一些端倪,某线程把一个共享变量A变成了B然后又变回了A。具体来说说:(需联系到CAS的思想)
比较并交换,线程要修改共享变量时,需先判断主内存中的变量值是否与自己的工作内存中的值相同,若相同才改变,反之继续判断。
举个栗子:
线程1,和线程2,都是”主内存“公司的员工,“主内存”公司中有一笔资金(即共享变量)用于公司之后的发展。
线程1和线程2都交替拥有的使用和监控权限。这里的交替是指线程的抢占执行。两人每次使用的时候需要先确定资金数目,没有改变才能继续使用,如果改变了需要再次确定。
但是线程2最近看中了一个短期项目,然后他就在自己的工作时间里,挪用公款,然后在项目获利之后,又把空给补上了。
之后线程1上班时,看了看自己知道的公司资金是没有变的,然后对他进行操作。

那么这个线程2是不是就钻了空子了呢。用公司的钱给自己赚钱。不得不说,他真是个小机灵鬼儿。但是这肯定是违反公司的规定的。所以公司就需要对自己内部的数据进行优化。也就是ABA问题的解决了。

公司的新规定,每个人操作资金的时候,都会有版本,也就是说这个资金被操作过几次。这样因为自己的任何一次操作都会被系统记录在案。我把它理解为操作的公开化。这样ABA问题就可以被解决了。
因此,在使用CAS前要考虑清楚“ABA”问题是否会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效。


import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class ABADemo {
    static AtomicReference<Integer> atomicReference =new AtomicReference<>(100);
    static AtomicStampedReference atomicStampedReference=new AtomicStampedReference(100, 1);
    public static void main(String[] args) throws InterruptedException {
        System.out.println("======以下是ABA问题的产生========");
        new Thread(()->{
            atomicReference.compareAndSet(100, 102);
            System.out.println(atomicReference.get());
            atomicReference.compareAndSet(102, 100);
            System.out.println(atomicReference.get());


        },"线程1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100, 1202));

            System.out.println(atomicReference.get());


        },"线程2").start();
        TimeUnit.SECONDS.sleep(3);
        System.out.println("=============以下是ABA问题的解决===========");
        new Thread(()->{
            int stamp =atomicStampedReference.getStamp();
            //为了让线程4也获得初始的版本号
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(100, 101, stamp, stamp+1);
            System.out.println("第一次操作后版本号为"+atomicStampedReference.getStamp());

            atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);
            System.out.println("第二次操作后版本号为"+atomicStampedReference.getStamp());

        },"线程3").start();

         new Thread(()->{
             int stamp =atomicStampedReference.getStamp();
             try {
                 TimeUnit.SECONDS.sleep(3);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             System.out.println("是否更新成功?  "+atomicStampedReference.compareAndSet(100, 1202,stamp,stamp+1));
             System.out.println("此时的值为   "+atomicStampedReference.getReference());
             System.out.println("此时版本号"+atomicStampedReference.getStamp());
             }, "线程4").start();


    }


}


运行结果:

======以下是ABA问题的产生========
102
100
true
1202
=============以下是ABA问题的解决===========
第一次操作后版本号为2
第二次操作后版本号为3
是否更新成功?  false
此时的值为   100
此时版本号3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值