CAS如何解決ABA問題


點贊再看,養成習慣,微信搜索「小大白日志」關注這個搬磚人。

文章不定期同步公眾號,還有各種一線大廠面試原題、我的學習系列筆記。

CAS如何解決ABA問題

什么是ABA:在CAS過程中,線程1、線程2分別從內存中拿到了當前值為A,同時線程2把當前值A改為B,隨后又把B改回來變為A,此后線程1檢查到當前值仍為A而導致執行cas成功,但這個過程卻發生了ABA問題,現場資源可能和當初不一樣了(線程2把當前值由A->B->A)

解決方法版本號機制,利用版本號標記線程1拿到的‘當前值’的版本,若線程2進行了A->B->A操作,則版本號會改變,那線程1再次拿到的‘當前值’的版本和第一次的肯定是不同的,從而判定cas失敗;

java代碼中AtomicStampedReference類的cas方法實現了版本號機制,可用它來解決ABA問題:

/**
 * @Description 測試解決CAS中的ABA問題
 * @Author afei
 * @date:2021/6/27
 */
public class AtomicStampedReferenceTest {
    //AtomicInteger和AtomicStampedReference的初始值都為100,但AtomicStampedReference帶了版本號0
    private static AtomicInteger atomicInt = new AtomicInteger(100);
    private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0);
    public static void main(String[] args) {
        //用AtomicInteger會產生ABA問題
        aba1();
        //用AtomicStampedReference解決cas過程中的ABA問題
        aba2();
 }

 public static void aba1(){
     Thread t1 = new Thread(new Runnable() {
         @Override
         public void run() {
             boolean c3 = atomicInt.compareAndSet(100, 101);
             System.out.println(c3); // true
         }
     });
     Thread t2 = new Thread(new Runnable() {
         @Override
         public void run() {
             //t2改變100->101->100,compareAndSet的參數:期待的值,新值
             atomicInt.compareAndSet(100, 101);
             atomicInt.compareAndSet(101, 100);
         }
     });
     t1.start();
     t2.start();
     try {
         //t1,t2執行完,主線程才能繼續執行
         t1.join();
         t2.join();
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
 }
  public static void aba2(){
      Thread t1 = new Thread(new Runnable() {
          @Override
          public void run() {
              int stamp = atomicStampedRef.getStamp();
              try {
                  TimeUnit.SECONDS.sleep(2);
              } catch (InterruptedException e) {
              }
              //compareAndSet中四個參數分別為:期待的值,新值,期待的版本號,新的版本號
              boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
              System.out.println(c3); // false
          }
      });
      Thread t2 = new Thread(new Runnable() {
          @Override
          public void run(){
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
              }
              //t2改變100->101->100,compareAndSet中四個參數分別為:期待的值,新值,期待的版本號,新的版本號
              atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
              atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
          }
      });
      t1.start();
      t2.start();
  }
}

AtomicInteger的原理

Java可以利用Syschronized、ReentrantLock、AtomicInteger類實現線程安全,AtomicInteger封裝了一個【volatile int value】屬性,它可以對這個屬性進行許多原子性操作,這些原子性操作大多是基於cas原理,而在cas中,AtomicInteger使用的是一個叫Unsafe的類中的方法,Unsafe可以提供一些底層操作,也就是CPU特定的指令集,進而避免了並發問題(Unsafe是一個很危險的類,它可以做一些和內存相關的操作)

OK,如果文章哪里有錯誤或不足,歡迎各位留言。
創作不易,各位的「三連」是二少創作的最大動力!我們下期見!


免責聲明!

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



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