并发中的ABA问题以及解决方案


一、ABA问题?

当我们使用CAS自旋的方式对一个值进行修改操作时,若修改成功就代表变量没有被修改过,真的是这样吗?
场景:
1. main主线程要修改变量的值A 修改为 值C,在修改之前会判断变量的值是否进行了修改
2. 在main主线程修改变量前,线程1把变量值A 修改为 值B
3. 接下来线程2main主线程前值B 修改为 值A
4. 轮到main主线程,主线程将变量修改之前判断,发现变量没有被改变还是 值A,那么main主线程修改成功,将变量 修改为 值C
5. 但是,变量的值被其他线程进行了更改,这就是ABA问题

	static AtomicReference<String> ref = new AtomicReference<>("A");

    public static void main(String[] args) throws Exception{
    
    
        System.out.println("main主线程 start...");
        //获取值A
        String prev =ref.get();
        other();
        Thread.sleep(1000);
        //尝试将值A 修改为 值C
        ref.compareAndSet(prev, "C");
        System.out.println("main主线程修改值后的newVal===>"+ref.get());

    }
    public static void other()throws Exception{
    
    
        new Thread(()->{
    
    
            //t1线程将值改成B
            ref.compareAndSet(ref.get(),"B");
            System.out.println("t1线程修改值后的newVal===>"+ref.get());
        },"t1").start();
        Thread.sleep(500);
        new Thread(()->{
    
    
            //t2线程将值回改为A
            ref.compareAndSet(ref.get(),"A");
            System.out.println("t2线程修改值后的newVal===>"+ref.get());

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

上方文字以及代码的执行结果如下图
在这里插入图片描述

二、解决ABA问题

使用原子引用在CAS自旋的基础上添加版本号

	static AtomicStampedReference<String> ref = new AtomicStampedReference<String>("A",0);

    public static void main(String[] args) throws Exception{
    
    
        System.out.println("main主线程 start...");
        //获取值A
        String prev = ref.getReference();
        //获取版本号
        int version = ref.getStamp();
        other();
        Thread.sleep(1000);
        //对比值和版本号 尝试将值A 修改为 C
        boolean result=ref.compareAndSet(prev, "C",version,version+1);
        System.out.println("main主线程修改值后的A===>B "+result);

    }
    public static void other()throws Exception{
    
    
        new Thread(()->{
    
    
            //t1线程获取版本号
            int version = ref.getStamp();
            //t1线程将值改成B
            boolean result=ref.compareAndSet(ref.getReference(),"B",version,version+1);
            System.out.println("t1线程修改值后A===>B "+result);
        },"t1").start();
        Thread.sleep(500);
        new Thread(()->{
    
    
            //t2线程获取版本号
            int version = ref.getStamp();
            //t2线程将值回改为A
            boolean flag=ref.compareAndSet(ref.getReference(),"A",version,version+1);
            System.out.println("t2线程修改值后B===>A "+flag);

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

执行结果:
在这里插入图片描述

  1. 另外一种简洁的方式来解决ABA问题
    使用AtomicMarkableReference类,与AtomicStampedReference类似,只不过AtomicMarkableReference使用的是通过true或者false来判断变量的值是否被其他线程修改

总结

浅谈ABA问题以及解决方案。

猜你喜欢

转载自blog.csdn.net/m0_50677223/article/details/131015269