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问题的解决方案则是使用时间戳原子引用,在原子引用上新增版本号(类似时间戳)