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();
}
}