ABA问题
假设这样一种场景,当第一个线程执行CAS(V,E,U)操作。在获取到当前变量V,准备修改为新的值U前,另外两个线程已连续修改了两次变量V的值,使得该值又恢复为旧值,这样的话,我们就无法正确判断这个变量是否已被修改过,
示例ABA问题:
public static void ABA(){
AtomicInteger stock = new AtomicInteger(1000);
System.out.println("(A)庫存余数:" + stock.get());
// 庫存A进3000
Integer newValue = stock.addAndGet(3000);
stock.compareAndSet(stock.get(), newValue);
System.out.println("(B)庫房A发3000:" + stock.get());
// 库管B发3000
stock.addAndGet(-3000);
System.out.println("(C)庫房調用取3000:" + stock.get());
// 庫管A查库存
if (stock.get() > 3000) {
newValue = stock.addAndGet(-3000);
System.out.println("(D)庫房A查庫存:" + stock.get());
} else {
System.out.println("(E)庫房A排查問題:" + stock.get());
}
}
遇到ABA问题是可以借助AtomicMarkableReference、AtomicStampedReference这两个类解决
代码示例:
/**
* 使用AtomicStampedReference实现库存管理的示例方法。
* AtomicStampedReference用于维护一个引用和一个时间戳,这里用于表示库存数量和操作序列号。
* 该方法模拟了库存增加和减少的过程,并检查了库存操作是否按预期执行。
*/
public static void ABA1(){
// 初始化库存参考值为1000,时间戳为0
AtomicStampedReference<Integer> stock = new AtomicStampedReference(1000,0);
System.out.println("(A)庫存余数:" + stock.getReference());
// 尝试将库存增加到4000,时间戳加1,模拟库存A进3000的操作
// 庫存A进3000
stock.compareAndSet(stock.getReference(),3000+1000,stock.getStamp(), stock.getStamp()+1);
System.out.println("(B)庫房A发3000:" + stock.getReference());
// 尝试将库存减少到1000,时间戳加1,模拟库存B发3000的操作
// 库管B发3000
stock.compareAndSet(stock.getReference(),4000-3000,stock.getStamp(), stock.getStamp()+1);
System.out.println("(C)庫房調用取3000:" + stock.getReference());
// 检查库存是否大于3000,用于验证库存操作的正确性
// 庫管A查库存
if (stock.getReference() > 3000) {
System.out.println("(D)庫房A查庫存:" + stock.getReference());
} else {
// 如果库存不满足预期且时间戳大于1,说明可能存在ABA问题,进行进一步检查
if(stock.getStamp()>1) {
System.out.println("(E)庫房A排查問題:" + stock.getReference());
}
}
}