CAS
CAS是英文單詞CompareAndSwap的縮寫,中文意思是:比較並替換。CAS需要有3個操作數:內存地址V,舊的預期值A,即將要更新的目標值B。
CAS指令執行時,當且僅當內存地址V的值與預期值A相等時,將內存地址V的值修改爲B,否則就什麼都不做。整個比較並替換的操作是一個原子操作。
代碼演示:
public class CASDemo {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//期望、更新
//public final boolean compareAndSet(int expect,int update)
//如果我期望的值達到了,那麼就更新,否則就不更新,CAS是CPU的併發原語
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
atomicInteger.getAndIncrement();
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}
//運行結果
//2021
//false
//2021
unsafe類
CAS:比較當前工作內存中的值和主內存中的值,如果這個值時期望的,那麼則執行操作,否則就一直循環!
缺點:
- 循環會耗時
- 一次性只能保證一個共享變量的原子性
- ABA問題
CAS:ABA問題(狸貓換太子)
public class CASDemo {
// CAS compareAndSet : 比較並交換!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//期望、更新
//public final boolean compareAndSet(int expect,int update)
//如果我期望的值達到了,那麼就更新,否則就不更新,CAS是CPU的併發原語
//==============搗亂的線程===================
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
//==============期望的線程===================
System.out.println(atomicInteger.compareAndSet(2020, 6666));
System.out.println(atomicInteger.get());
}
}
解決ABA問題,引入原子引用
帶版本號的原子操作
下面代碼裏注意我們用的是Integer,所以數字在-128~127之間,不能用上面例子裏的2020,2021,666數字。
public class CASDemo {
//AtomicStampedReference 注意,如果泛型是一個包裝類,注意對象的引用問題
// 正常在業務操作,這裏面比較的都是一個個對象
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1,1);
// CAS compareAndSet : 比較並交換!
public static void main(String[] args) {
new Thread(()->{
int stamp = atomicStampedReference.getStamp(); // 獲得版本號
System.out.println("a1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Lock lock = new ReentrantLock(true);
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println("a2=>"+atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(2, 1,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("a3=>"+atomicStampedReference.getStamp());
},"a").start();
// 樂觀鎖的原理相同!
new Thread(()->{
int stamp = atomicStampedReference.getStamp(); // 獲得版本號
System.out.println("b1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(1, 6,
stamp, stamp + 1));
System.out.println("b2=>"+atomicStampedReference.getStamp());
},"b").start();
}
}
本文爲學習狂神說的JUC課程做的筆記,課程地址:https://www.bilibili.com/video/BV1B7411L7tE?p=32