ABA问题的产生和解决方案

package CASDemo;

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); //原子引用,初始值为100
    static AtomicStampedReference<Integer> atomicStampedReference=new AtomicStampedReference<>(100,1);//时间戳原子引用,初始值为100,时间戳(版本号)为1

    public static void main(String[] args) {
        System.out.println("===========以下是ABA问题的产生=============");
        new Thread(() ->{
            atomicReference.compareAndSet(100,101);
            atomicReference.compareAndSet(101,100);
        },"t1").start();

        new Thread(() ->{
            //暂停1秒钟t2线程,保证上面的t1线程完成了一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(atomicReference.compareAndSet(100,2019)+"\t"+atomicReference.get());
        },"t2").start();


        //暂停一会儿线程,进行ABA问题的解决
        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        
 //ABA问题的解决  AtomicStampedReference
        System.out.println("======以下是ABA问题的解决=========");
        new Thread(() ->{
            int stamp=atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t第一次版本号:"+stamp);
            //暂停1秒钟3线程
            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);
            //暂停3秒钟4线程,保证上面的t3线程完成了一次ABA操作
            try {
                TimeUnit.SECONDS.sleep(3);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            boolean result=atomicStampedReference.compareAndSet(100,2019,stamp,stamp+1);

            System.out.println(Thread.currentThread().getName()+"\t修改成功否:"+result+"\t当前最新实际版本号:"+atomicStampedReference.getStamp()+"\tt4线程的版本号:"+stamp);

            System.out.println(Thread.currentThread().getName()+"\t当前实际最新值:"+atomicStampedReference.getReference());
        },"t4").start();

    }

}

程序输出结果:

===========以下是ABA问题的产生=============
true	2019
======以下是ABA问题的解决=========
t3	第一次版本号:1
t4	第一次版本号:1
t3	第二次版本号:2
t3	第三次版本号:3
t4	修改成功否:false	当前最新实际版本号:3	t4线程的版本号:1
t4	当前实际最新值:100

根据上述代码,所谓ABA问题,通俗地说就是 CAS算法的方式只对比值是否正确,不关心过程是否被修改过,尽管CAS的操作成功,但是这个过程存在隐患。
比如上述ABA问题的产生的例子:线程t1和线程t2要对同一个值进行操作,线程t1运行时间较短,线程t2运行时间较长,t1、t2同时在主内存中获取值到自己的工作内存。由于t1运行时间短,t1进行了一顿操作后,又将主内存的值改回了原来的值,但是过程中程序已经被t1产生了改变。t1结束后,t2操作时对比主内存的值发现一致则以为程序没有被改动过,t2操作执行成功。
ABA问题的解决方案则是使用时间戳原子引用,在原子引用上新增版本号(类似时间戳)

发布了18 篇原创文章 · 获赞 6 · 访问量 524

猜你喜欢

转载自blog.csdn.net/weixin_41143657/article/details/105724521