引言
CAS的缺点包括循环判断真实值和主内存的值时会使CPU资源损耗大、只能对一个共享变量执行CAS、ABA问题。
CAS算法实现的一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。
比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且线程two进行了一些操作将值变成了B,然后线程two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,但实际上是发生了变化。
尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
解决
新增版本号机制。java也提供了一个类,AtomicStampedReference。
package cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
//相比之前新增了一个版本号,假设初始值是100,版本号是1
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
System.out.println("=================ABA问题产生=================");
new Thread(()->{
//如果当前值是100,修改为101
atomicReference.compareAndSet(100,101);
//如果当前值是101,修改为100
atomicReference.compareAndSet(101,100);
},"t1").start();
new Thread(()->{
//暂停1秒钟t2线程,保证t1完成了一次ABA操作
try{
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100,2019)+"\t"+atomicReference.get());
},"t2").start();
try{
TimeUnit.SECONDS.sleep(2);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("=================ABA问题解决=================");
new Thread(()->{
//初始版本号 1
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第1次版本号: "+stamp);
try{
TimeUnit.SECONDS.sleep(1);
}catch(InterruptedException e){
e.printStackTrace();
}
//修改后变成 100 1 --> 101 2
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第2次版本号: "+atomicStampedReference.getStamp());
//101 2 --> 100 3
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第3次版本号: "+atomicStampedReference.getStamp());
},"t3").start();
new Thread(()->{
//初始版本号 1
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第1次版本号: "+stamp);
//暂停1秒钟t2线程,保证t3完成了一次ABA操作
try{
TimeUnit.SECONDS.sleep(3);
}catch(InterruptedException e){
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100, 2021, stamp, stamp + 1);
System.out.println(Thread.currentThread().getName()+"\t 是否修改成功: "+result+"\t 当前最新实际版本号"+atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName()+"\t 当前线程实际最新值:"+atomicStampedReference.getReference());
},"t4").start();
}
}