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-0第2次版本号:2
Thread-1第一次版本号:2
Thread-0第3次版本号:3
Thread-1 修改是否成功:true当前版本号:4:当前实际值:12
AtomicStampedReference的cas必须reference和stamp都等于期望值,才会进行修改。因此对于ABA问题,虽然线程2判断reference还是A,但是此时版本号已经不满足要求了。因此并不会执行set。