ABA问题的产生
CAS算法实现的核心是需要取出内存中某个时刻的数据并在当下时刻比较并替换。那么在这个时间差类会导致数据的变化。比如一个线程one从内存位置v取值a,这时候另一个线程two也从内存中取出a,并且线程two进行了一些操作将值变成了b。然后two又将v位置的数据变成了a。这时候线程one进行cas操作发现内存中任然是a。然后线程one操作成功。尽管线程one的cas操作成功了,但是不代表这个过程就是没有问题的。
ABA问题示例一:
public class AtomicStampedReferenceDemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
public static void main(String[] args) {
new Thread(()->{
//将100替换成101了,然后又把101变成100,因为线程B暂停了1妙钟。没有发现线程A值已经变换过,于是变换成功。
atomicReference.compareAndSet(100,101);
atomicReference.compareAndSet(101,100);
},"线程A").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicReference.compareAndSet(100,2019);
System.out.println(atomicReference.get());
},"线程B").start();
}
}
ABA问题示例二:
如上图所示,现有一个用单向链表实现的堆栈,栈顶为A,这时线程T1已经知道 A next为B,然后希望用CAS将栈顶替换为B
head. compareAndSet(A, B)
在T执行上面这条指令之前,线程T介入,将A、B出栈,再 pushD、C、A,此时堆栈结构如下图,而对象B此时处于游离状态
此时轮到线程T1执行CAS操作,检测发现栈顶仍为A,所以CAS成功,栈顶变为B,但实际上B,next为nul,所以此时的情况变为
其中堆栈中只有B一个元素,C和D组成的链表不再存在于堆栈中,平白无故就把C、D丢掉了。
ABA问题的解决
aba问题解决代码示例
public class AtomicStampedReferenceDemo {
private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
new Thread(() -> {
System.out.println("线程A第1次版本号:" + atomicStampedReference.getStamp());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println("线程A第2次版本号:" + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println("线程A第3次版本号:" + atomicStampedReference.getStamp());
}, "线程A").start();
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println("线程B第1次版本号:" + atomicStampedReference.getStamp());
//睡眠3秒,是为了让线程A完成ABA操作
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程B第2次版本号:" + atomicStampedReference.getStamp());
boolean success = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
System.out.println(success);
System.out.println(atomicStampedReference.getReference() + "---" + atomicStampedReference.getStamp());
}, "线程B").start();
}
}