1、CAS 即 Compare -And- Swap 比较并交换;
2、 通过AtomicInteger.compareAndSet();方法能够解决多线程模式下i++计算结果出现的数据不一致的问题。
/**
* @program: mybatis
* @description: CAS
* @author: Miller.FAN
* @create: 2019-11-11 18:24
**/
public class CASDome {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.compareAndSet(5,2018)+"/t current data " + atomicInteger.get());
}
}
3、AtomicInteger.compareAndSet()底层实现是什么?
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
4、unsafe.compareAndSwapInt(this, valueOffset, expect, update)的底层是什么样的?
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
上面这段代码就有意思了,他是Unsafe类种的一个方法,Unsafe中的方法都是直接调用操作系统底层资源执行相应的任务,即操作系统底层原语,不容许被打断,不会造成所谓的数据不一致的问题。
var5 = this.getIntVolatile(var1, var2);中var1指当前对象,var2内存偏移地址。结果var5就是获得的内存中的数据。
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); // 拿一下主内存地址中的值与自己保存的快照对比,如果一致就修改,如果不一致放弃修改,再做一次var5 = this.getIntVolatile(var1, var2);,直到主内存中的值与自己的快照值一直,海海皮皮的进行修改跳出循环,return var5+1;
5、为什么不直接使用Synchronized,而使用CAS呢?
杀鸡焉用牛刀?Synchronized是重锁,并发效率低。
6、CAS的缺点是什么?
循环时间长,CPU开销大。只能保证一个共享变量的一致性操作。不可避免的ABA问题。
7、ABA问题的演示和解决
public class AutmicRefence {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
//ABA问题的演示
new Thread(()-> {
atomicReference.compareAndSet(100,101);
atomicReference.compareAndSet(101,100);
},"t1 ").start();
new Thread(()-> {
try{
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("current thread :" + atomicReference.compareAndSet(100,2049) + "/t current value: "+atomicReference.get());
},"t2").start();
//ABA问题的解决
new Thread(()-> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "/t 第一次的本版号" + stamp);
try{
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName() + "/t 第二次的本版号" + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName() + "/t 第三次的本版号" + atomicStampedReference.getStamp());
},"t3 ").start();
new Thread(()-> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "/t 第一次的本版号" + stamp);
try{
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
e.printStackTrace();
}
boolean ret = atomicStampedReference.compareAndSet(100,101,stamp,stamp+1);
System.out.println(Thread.currentThread().getName() + "/t 第二次的本版号" + atomicStampedReference.getStamp());
System.out.println("修改成功否?" + ret);
System.out.println("当前值是多少?" + atomicStampedReference.getReference());
},"t4 ").start();
}
}