CAS带来的ABA问题
线程1和线程2同时对变量i = 1 进行操作,线程1对i进行了两次操作,先将i+1 写出,后又进行了i-1并写出,线程2过来读的时候与原来值1仍然是相等的,虽然值仍然相等,但是i的值却发生过改变,期间从1变成2又变成1(ABA问题)。
解决方案
对值的每次操作都进行记录,需要保证是一个原子操作。
java中使用AtomicMarkableReference或者AtomicStampedReference。
AtomicMarkableReference:使用boolean表示是否修改过。
AtomicStampedReference: 使用int 记录改变的次数(推荐)!
AtomicStampedReference 使用:
java">class Stu {private String name;private Integer age;
getter/setter
}//参数1: 对象
//参数2:初始版本号AtomicStampedReference<Stu> accountBalance = new AtomicStampedReference<>(stu, 0);源码:
public AtomicStampedReference(V var1, int var2) {this.pair = AtomicStampedReference.Pair.of(var1, var2);
}
static <T> Pair<T> of(T var0, int var1) {return new Pair(var0, var1); //底层使用Pair对象。
}private Pair(T var1, int var2) {this.reference = var1; //真实对象this.stamp = var2; //版本号
}
java">Thread threadA = new Thread(() -> {int[] stampHolder = new int[1];//获取对象Stu stu1 = accountBalance.get(stampHolder);//改变的次数int currentStamp = stampHolder[0];System.out.println("Thread A: 当前年龄 = " + stu1.getAge() + ", 当前版本号= " + currentStamp);stu1.setAge(100000);boolean success = accountBalance.compareAndSet(stu1, stu1, currentStamp, currentStamp + 1);/***源码:* 参数1: 期望值* 参数2: 新值* 参数3: 原有修改次数* 参数4: 新修改次数(一般使用原有修改次数+1)* public boolean compareAndSet(V var1, V var2, int var3, int var4) {** //原始对象值 包含对象和修改的次数* Pair var5 = this.pair;* return* var1 == var5.reference //对象不是同一个(期望值不一样) 就不再进行操作* && var3 == var5.stamp // 版本号不一样 就不再进行操作* && (var2 == var5.reference && var4 == var5.stamp //操作对象与原来一样并且操作次数也一样 表示中间的值没有被其他线程修改过,无需继续修改* ||* this.casPair(var5, AtomicStampedReference.Pair.of(var2, var4))); //进行CAS操作* }******/if (success) {System.out.println("Thread A: 更新成功,新的年龄 = " + accountBalance.getReference().getAge() + ",新的版本号" + accountBalance.getStamp());} else {System.out.println("Thread A: 更新失败");}});