一个线程把数据A变为了B,然后又重新变成了A。此时另外一个线程读取的时候,发现A没有变化,就误以为是原来的那个A。这就是有名的ABA问题。ABA问题会带来什么后果呢?我们举个例子。
一个小偷,把别人家的钱偷了之后又还了回来,还是原来的钱吗,你老婆出轨之后又回来,还是原来的老婆嘛?ABA问题也一样,如果不好好解决就会带来大量的问题。最常见的就是资金问题,也就是别人如果挪用了你的钱,在你发现之前又还了回来。但是别人却已经触犯了法律。
如何去解决这个ABA问题呢,就是使用下面的AtomicStampedReference
。
使用一个版本号去维护,如果发现版本不一致就不更新
class Test {static AtomicInteger balance = new AtomicInteger(100);static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 0);public static void main(String[] args) throws InterruptedException {System.out.println("ABA问题:当前值" + balance.get());//模拟主线程改动变量时,其他线程更改了变量new Thread(() -> System.out.println("thread1:100改成90," + balance.compareAndSet(100, 90))).start();TimeUnit.SECONDS.sleep(1);new Thread(() -> System.out.println("thread2:90改成100," + balance.compareAndSet(90, 100))).start();TimeUnit.SECONDS.sleep(1);//上面的线程改成别的值,然后又改回来,但是主线程没发现,以为是原来的,所以更改成功了System.out.println("main1:100改成90," + balance.compareAndSet(100, 90));System.out.println("\nABA问题解决:当前值" + atomicStampedReference.getReference());int mstamp = atomicStampedReference.getStamp();System.out.println("main2:当前版本:" + mstamp);//模拟主线程改动变量时,其他线程更改了变量new Thread(() -> {int stamp = atomicStampedReference.getStamp();System.out.println("thread3:100改成90,当前版本=" + atomicStampedReference.getStamp() + "," + atomicStampedReference.compareAndSet(100, 90, stamp, stamp + 1));}).start();TimeUnit.SECONDS.sleep(1);new Thread(() -> {int stamp = atomicStampedReference.getStamp();System.out.println("thread4:90改成100,当前版本=" + atomicStampedReference.getStamp() + "," + atomicStampedReference.compareAndSet(90, 100, stamp, stamp + 1));}).start();TimeUnit.SECONDS.sleep(1);//期间mstamp被更改过,所以这里更新失败System.out.println("main2:100改成90,当前版本=" + atomicStampedReference.getStamp() + "," + atomicStampedReference.compareAndSet(100, 90, mstamp, mstamp + 1));}}
结果
ABA问题:当前值100
thread1:100改成90,true
thread2:90改成100,true
main1:100改成90,trueABA问题解决:当前值100
main2:当前版本:0
thread3:100改成90,当前版本=0,true
thread4:90改成100,当前版本=1,true
main2:100改成90,当前版本=2,false