CAS机制的ABA问题及解决

1、什么是ABA问题

ABA问题:就是说一个线程把数据A变成了B,然后又重新变成了A。此时另一个线程读取的时候发现A没有变化,就误以为还是原来的A。

一个ABA例子

public class testABA {
    
    
    private static AtomicInteger index=new AtomicInteger(10);
    public static void main(String[] args) {
    
    
        new Thread(()->{
    
    
            index.compareAndSet(10,11);
            index.compareAndSet(11,10);
            System.out.println(Thread.currentThread().getName()+":10->11->10");
        },"t1").start();

        new Thread(()->{
    
    
            try {
    
    
                TimeUnit.SECONDS.sleep(2);
                boolean b = index.compareAndSet(10, 12);
                System.out.println(Thread.currentThread().getName()+"index的预期是10吗?"+b
                +"    设置的新值是:"+index.get());
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

        },"t2").start();
    }
}
//运行结果
t1:10->11->10
t2index的预期是10吗?true设置的新值是:12

2、解决办法AtomicStampedReference

public class testABA {
    
    
    private static AtomicInteger index=new AtomicInteger(10);    
    static AtomicStampedReference<Integer> stampref=new AtomicStampedReference<>(10,1);

    public static void main(String[] args) {
    
    
        new Thread(()->{
    
    
            int stamp=stampref.getStamp();
            System.out.println(Thread.currentThread().getName()+"第一次版本号:"+stamp);
            stampref.compareAndSet(10,11,stampref.getStamp(),stampref.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"第2次版本号:"+stampref.getStamp());
            stampref.compareAndSet(11,10,stampref.getStamp(),stampref.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"第3次版本号:"+stampref.getStamp());

        }).start();


        new Thread(()->{
    
    
            System.out.println(Thread.currentThread().getName()+"第一次版本号:"+stampref.getStamp());
            try {
    
    
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            boolean b = stampref.compareAndSet(10, 12, stampref.getStamp(), stampref.getStamp() + 1);
            System.out.println(Thread.currentThread().getName()+" 修改是否成功:"+b+"当前版本号:"+stampref.getStamp()+":当前实际值:"+stampref.getReference());

        }).start();
    }
}
//运行结果
Thread-0第一次版本号:1
Thread-02次版本号:2
Thread-1第一次版本号:2
Thread-03次版本号:3
Thread-1 修改是否成功:true当前版本号:4:当前实际值:12

AtomicStampedReference的cas必须reference和stamp都等于期望值,才会进行修改。因此对于ABA问题,虽然线程2判断reference还是A,但是此时版本号已经不满足要求了。因此并不会执行set。

猜你喜欢

转载自blog.csdn.net/qq_37935909/article/details/108812758