什么是ABA问题
在CAS算法中,需要取出内存中某时刻的数据(由用户完成),在下一时刻比较并交换(CPU保证原子操作),这个时间差会导致数据的变化。
1、线程1从内存位置V中取出A
2、线程2从内存位置V中取出A
3、线程2进行了写操作,将B写入内存位置V
4、线程2将A再次写入内存位置V
5、线程1进行CAS操作,发现V中仍然是A,交换成功
尽管线程1的CAS操作成功,但线程1并不知道内存位置V的数据发生过改变
ABA问题本质:
根本在于CAS在修改变量的时候,无法记录变量的状态,比如是否修改过这个变量,修改的次数。
public class ABADemo { private static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100); public static void main(String[] args) { new Thread(() -> { Integer a = atomicReference.get(); atomicReference.compareAndSet(a, 101); atomicReference.compareAndSet(101, a); System.out.println("t1修改了atomicReference的值"); },"t1").start(); new Thread(() -> { Integer b = atomicReference.get(); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(atomicReference.compareAndSet(b, 2019) + " 修改后的值:" + atomicReference.get()); },"t2").start(); } }
ABA问题怎么解决
public class AtomicStampedReferenceTest { private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1); public static void main(String[] args) { new Thread(() -> { Integer stamp = atomicStampedReference.getStamp(); System.out.println("t1拿到的初始版本号:" + stamp); //睡眠1秒,是为了让t2线程也拿到同样的初始版本号 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(100, 101,stamp,stamp+1); stamp = atomicStampedReference.getStamp(); atomicStampedReference.compareAndSet(101, 100,stamp,stamp+1); },"t1").start(); new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println("t2拿到的初始版本号:" + stamp); //睡眠3秒,是为了让t1线程完成ABA操作 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("最新版本号:" + atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet(100, 2019,stamp,atomicStampedReference.getStamp() + 1) + " 当前 值:" + atomicStampedReference.getReference()); },"t2").start(); } }
如果不关心引用变量更改了几次,只单纯的关心是否更改过,可以使用AtomicMarkableReference
public class ABADemo { private static AtomicMarkableReference<Integer> atomicMarkableReference = new AtomicMarkableReference<Integer>(100,false); public static void main(String[] args) { new Thread(() -> { System.out.println("t1版本号是否被更改:" + atomicMarkableReference.isMarked()); //睡眠1秒,是为了让t2线程也拿到同样的初始版本号 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } atomicMarkableReference.compareAndSet(100, 101,false,true); },"t1").start(); new Thread(() -> { boolean isMarked = atomicMarkableReference.isMarked(); System.out.println("t2版本号是否被更改:" + isMarked); //睡眠3秒,是为了让t1线程执行 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("是否更改过:" + atomicMarkableReference.isMarked()); System.out.println(atomicMarkableReference.compareAndSet(100, 2019,isMarked,true) + " 当前 值:" + atomicMarkableReference.getReference()); },"t2").start(); } }