深入理解CAS,解決ABA問題

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:比較當前工作內存中的值和主內存中的值,如果這個值時期望的,那麼則執行操作,否則就一直循環!
缺點:

  1. 循環會耗時
  2. 一次性只能保證一個共享變量的原子性
  3. 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章