介绍
AtomicStampedReference是jdk提供的用于cas操作引用类型的一个类,相比于AtomicReference而已可以解决ABA问题,解决的原理就是用两个参数来对比是否做了更改,而第二个参数是个int值,只要保证第二个int值一直累加即不会存在aba问题
使用
场景:对一个Integer类型的对象cas自增
public class CasDemo {
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(1,1);
public static void main(String[] args) {
// 必定成功,原子性操作
boolean result = increment();
System.out.println(result);
}
/**
* 自增+1
*/
public static boolean increment() {
// 死循环,直到cas成功
for (;;) {
// 必须先get出来,不能在compareAndSet里面在get,否则不能保证多个get获取到的是不同值
Integer i1 = atomicStampedReference.getReference();
int s1 = atomicStampedReference.getStamp();
Integer i2 = i1 + 1;
int s2 = atomicStampedReference.getStamp() +1;
boolean result = atomicStampedReference.compareAndSet(i1,i2,s1,s2);
// 失败则继续循环
if (result) {
return true;
}
}
}
}
ABA问题
ABA问题是CAS机制中出现的一个问题,就是说一个线程把数据A变为了B,然后又重新变成了A。此时另外一个线程读取的时候,发现A没有变化,就误以为是原来的那个A。这就是有名的ABA问题。
案例
如共享变量A初始值1,线程1需要对A做自增1,线程2需要把A修改为10,线程3需要把A修改为1
1、线程1读取到A的值是1,此时还没有执行自增+1,线程2和线程3并发执行了
2、线程2把A的值修改为了10
3、线程3把A的值修改为了1
4、此时线程1准备对A的值+1,线程A执行CAS(Compare and Swap,即比较再交换),比较时发现A的值是之前读取到的值1,发现相等,执行+1,执行结束
此时虽然线程1执行成功了,但是线程A执行比较时的A的状态其实并不是线程1之前读取到的A的状态了,这就是ABA问题
ABA问题导致的原因,是CAS过程中只简单进行了“值”的校验,再有些情况下,“值”相同不会引入错误的业务逻辑(例如库存),有些情况下,“值”虽然相同,却已经不是原来的数据了。
并不是所有场景在出现ABA都会存在问题,如果上述场景就不会,线程1只不过是想对A自增1,上述场景即使出现ABA问题,仍然不影响业务
至于ABA问题在哪些场景需要避免,还是需要根据具体业务判断
扩展
对于一些常见类型推荐使用相对应的cas类,如AtomicInteger、AtomicLong、AtomicBoolean
原创不易,转载请说明出处https://editor.csdn.net/md?articleId=107030155