1. 程式人生 > 其它 >CAS無鎖機制

CAS無鎖機制

1、 背景

  傳統Synchronized鎖:悲觀,如果沒有獲取到鎖的情況下,會讓當前執行緒變為阻塞的狀態,釋放CPU執行權,效率非常低。

  樂觀鎖(自旋):本質上沒有鎖,沒有死鎖現象,而且效率比較高,不會釋放CPU執行權,自旋並通過預值比較或版本號控制。

2、原理

  CAS的英文全稱是CompareAndSet,也就是比較然後修改,涉及到三個值,V、E和N,V是主記憶體的共享變數值,E是工作記憶體的副本值,N是修改記憶體的值。關於記憶體值的修改,有兩種情況:第一種情況是比較V和E的值,如果相等,則對V進行修改;第二種情況是V和E不等,說明有執行緒已經修改過了,那就重新讀取主記憶體的值,再做判斷、修改。
V=記憶體值(主記憶體值);E=預期值(工作記憶體值) ;N=新值(需要修改的值)
  第一種情況:(V未被修改)
    第一步:讀取主記憶體值V,複製給E
    第二步:判斷V的值,如果V==E,說明共享變數的V沒有被修改
    第三步:將V的值改為N
  第二種情況:(V被修改)
    第一步:讀取記憶體之V,複製給E
    第二步:判斷V的值,如果V!=E,說明別的執行緒修改了共享變數的V
    第三步:重新讀取主記憶體V的值給E
    第四步:修改時再判斷V的值是否等於E,如果不等,繼續自旋,直至相等再將V的值修改為N。

  注意:不可能有兩個執行緒同時修改V的值,因為CAS底層通過指令控制了原子性。

3、應用場景

  Java UNSAFE類
  原子類 Atomic

4、利用CAS原子類方式實現一個鎖

 1 public class AtomicTryLock {
 2 
 3     /**
 4      * 定義AtomicInteger  修改為1表示該鎖已經被使用該 修改為0表示為被使用
 5      */
 6     private volatile AtomicInteger atomicInteger = new AtomicInteger(0);
 7     private Thread lockCurrentThread;
8 9 /** 10 * 嘗試獲取鎖 11 * 12 * @return 13 */ 14 public boolean tryLock() { 15 boolean result = atomicInteger.compareAndSet(0, 1); 16 if (result) { 17 lockCurrentThread = Thread.currentThread(); 18 } 19 return result; 20 } 21 22
/** 23 * 釋放鎖 24 * 25 * @return 26 */ 27 public boolean unLock() { 28 if (lockCurrentThread != null && lockCurrentThread != Thread.currentThread()) { 29 return false; 30 } 31 return atomicInteger.compareAndSet(1, 0); 32 } 33 34 public static void main(String[] args) { 35 AtomicTryLock atomicTryLock = new AtomicTryLock(); 36 IntStream.range(1, 10).forEach((i) -> new Thread(() -> { 37 38 try { 39 boolean result = atomicTryLock.tryLock(); 40 if (result) { 41 System.out.println(Thread.currentThread().getName() + ",獲取鎖成功~"); 42 } else { 43 System.out.println(Thread.currentThread().getName() + ",獲取鎖失敗~"); 44 } 45 } catch (Exception e) { 46 e.printStackTrace(); 47 atomicTryLock.unLock(); 48 } finally { 49 atomicTryLock.unLock(); 50 } 51 52 }).start()); 53 } 54 }

5、如何解決CAS產生的ABA問題

5.1 什麼是ABA問題

  如果【執行緒1】將原來的值A,改為了B,【執行緒2】B又改為了A 發現沒有發生變化,實際上已經發生了變化,但是【執行緒3】修改時判斷V值沒有發生變化,這種現象為ABA

5.2 解決辦法

  通過版本號碼,對每個變數更新的版本號碼做+1

來源:螞蟻課堂(mayikt.com)