CAS锁有ABA问题
因为CAS是乐观锁,只要预期值和当前值相同则可以修改成功,但是这会有个"ABA"问题.
什么是"ABA"问题呢,我上段程序就知道了
public class AtomicReferenceDemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
System.out.println("ABA问题的演示~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
new Thread(() -> {
//把值修改为101 再改为100
boolean b = atomicReference.compareAndSet(100, 101);
System.out.println(b+","+Thread.currentThread().getName()+"第一次修改值为"+atomicReference);
b = atomicReference.compareAndSet(101,100);
System.out.println(b+","+Thread.currentThread().getName()+"第二次修改值为"+atomicReference);
},"t1").start();
new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = atomicReference.compareAndSet(100, 2020);
System.out.println(b+ ","+Thread.currentThread().getName() +"修改值为"+atomicReference.get());
},"t2").start();
}
}
初始值就为100
上面这段程序t1和t2线程,t1线程修改值两次,第一次修改时预期值100,希望改为101,第二次预期值101,希望改为100,其实t1线程干的就是"ABA"操作.
t2线程一来看预期值100对上了,然后修改为2020,看上去是不是很和谐呢.这就是"ABA"问题,t1线程干了一波事情,神不知鬼不觉
控制台输出如下
ABA问题的解决
ABA问题解决也很简单,就是版本号的思路,修改一次版本号自增一次,虽然值还是那个值,但是版本号却一直在变.直接上段程序就清楚了
需要用到 AtomicStampedReference这个类,时间戳原子类
public class AtomicReferenceDemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
System.out.println("演示利用版本号解决ABA问题~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"第一次版本号:"+stamp);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = stampedReference.compareAndSet(100, 101, stampedReference.getStamp(),
stampedReference.getStamp() + 1);
System.out.println(b+ ","+Thread.currentThread().getName() +"修改值为"+stampedReference.getReference()+",版本号为"+stampedReference.getStamp());
b = stampedReference.compareAndSet(101, 100, stampedReference.getStamp(),
stampedReference.getStamp() + 1);
System.out.println(b+ ","+Thread.currentThread().getName() +"修改值为"+stampedReference.getReference()+",版本号为"+stampedReference.getStamp());
},"t4").start();
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"第一次版本号:"+stamp);
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean b = stampedReference.compareAndSet(100, 2020, stamp, stampedReference.getStamp() + 1);
System.out.println(b+ ","+Thread.currentThread().getName() +"当前版本号:"+stamp+"修改值为"+stampedReference.getReference()+",版本号为"+stampedReference.getStamp());
},"t5").start();
}
}
这段程序是t4和t5两个线程,t4干了一波ABA操作,但是版本号出卖了它,t5线程一看预期值100希望修改为2020,预期值确实符合,但是版本号1和当前版本号3一比对就漏出破绽了,于是修改失败.
输出结果如下
总结
ABA问题可以使用版本号解决
ABA问题主要细分为两个点
1.会有没必要的资源开销
2.数据会丢失覆盖的问题