CAS的ABA問題有什么危害?


CAS

CAS(CompareAndSet)是保證並發安全性的一條CPU底層原子指令,它的功能是判斷某個值是否為預期值,如果是的話,就改為新值,在CAS過程中不會被中斷。

compareAndSet 在JNI(Java Naive Interface)中實現,位於unsafe.cpp文件,關鍵的語句是 cmpxchg(x, addr, e),其中x指的是舊值,addr是要和oldValue一致的內存位置,而e是要變為的新值。執行該原子語句時,將oldValue和從addr取出的值進行比較,相等的話才設置addr位置的值為新值e。

ABA

但CAS存在一個ABA問題,舉例來說,假設線程1和線程2擁有同一個引用p,p指向對象A。某個時刻,線程1想要利用CAS把p指向的對象換成C,此時被線程2中斷,線程2將p指向的對象換成B后再換成A,然后線程1繼續運行,發現p確實仍然指向對象A,因此執行CAS將A換成C。但線程1並不知道在它中斷的這段時間內,p指向的引用經歷了從A到B在到A的過程,這個bug就稱為ABA問題。對於普通場景來說,ABA問題似乎不會造成什么危害,但我們來考慮下面這種場景。

ABA的危害

下面是一段偽代碼,將就着看一下。場景是用鏈表來實現一個棧,初始化向棧中壓入B、A兩個元素,棧頂head指向A元素。

在某個時刻,線程1試圖將棧頂換成B,但它獲取棧頂的oldValue(為A)后,被線程2中斷了。線程2依次將A、B彈出,然后壓入C、D、A。然后換線程1繼續運行,線程1執行compareAndSet發現head指向的元素確實與oldValue一致,都是A,所以就將head指向B了。但是,注意我標黃的那行代碼,線程2在彈出B的時候,將B的next置為null了,因此在線程1將head指向B后,棧中只剩了一個孤零零的元素B。但按預期來說,棧中應該放的是B → A → D → C。

 

Node head;
head = B;
A.next = head;
head = A;


Thread thread1 = new Thread(
    ->{
          oldValue = head;
          sleep(3秒);
          compareAndSet(oldValue, B);

    }
);

Thread thread2 = new Thread(
    ->{
        // 彈出A
          newHead = head.next;
          head.next = null; //即A.next = null;
          head = newHead;
         // 彈出B
          newHead = head.next;
          head.next = null; // 即B.next = null;
          head = newHead; // 此時head為null
          
          // 壓入C
          head = C;
          // 壓入D
          D.next = head;
          head = D;
          // 壓入A
          A.next = D;
          head = A;
          

    }
);

thread1.start();
thread2.start();

 

如有錯誤,敬請指正 >。<


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM