CAS算法原理分析(java中原子類如何實現線程安全)

1. 目標:

從原碼層面分析CAS算法、以及java.util.concurrent.atomic 包下的原子類是如何運用CAS算法而實現線程安全。

2. 基礎知識

CAS算法基本原理

CAS算法全稱:compare and swap (比較並交換),是CPU指令級的操作,只有一步原子操作,整個操作是原子的,也就是要麼不執行,要麼執行完。這樣的系統原子操作能做什麼呢?比較內存中的參數值和方法調用處提供的參數值,如果相等,則將內存的參數值,設置爲新值,否則返回內存中的參數值。

volatile 關鍵字

這個關鍵字修飾的變量,能保證變量在線程之間可見。再解釋下,如果2個線程同時操作一個被volatile關鍵字修飾的變量,各個線程都會將變量從內存統一拷貝到自己的所在cpu的緩存中,當一個線程對變量進行修改之後,該變量位於其他線程的cpu緩存中拷貝能立即感知到。並同時調整爲一致。

Unsafe 類

大家都知道java一般很少直接操作內存,但是其實還是有一個類是可以直接操作修改內存的,他就是Unsafe。要獲取一個對象的某個字段的值是多少,我們應該知道幾個重要參數,第一個是對象在內存中的起始位置,第二個是需要獲取的字段在內存中的相對地址,第三個是字段的長度,也就是需要取幾個字節,纔對應這個字段的值。

3. 從源碼角度分析AtomicInteger是如何實現線程安全的

AtomicInteger的成員變量介紹

 private static final Unsafe unsafe = Unsafe.getUnsafe();//用於操作內存的Unsafe實例
 private static final long valueOffset;//AtomicInteger所保存的int值在內存中的偏移量
 private volatile int value; //AtomicInteger中保存的int值,被volatile關鍵字修飾,確保線程之間可見。
 static {
        try {
            //實際設置了int值在內存中的偏移
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

下面是我們徵對常用的方法進行介紹

//在當前值的基礎上自動+1,並且返回新值
 public final int incrementAndGet() {
      return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

這裏我們看底層是調用了unsafe的getAndAddInt方法,我們跟進去

//obj 是AtomicInteger對象
//index long型的內存偏移量
//addVale 本次操作需要增加的值
public final int getAndAddInt(Object obj, long index, int addVale) {
        int oldValue;
        do {
            oldValue = this.getIntVolatile(obj, index);//先從內存裏取的參數的值
        } while(!this.compareAndSwapInt(obj, index, oldValue, oldValue + addVale));

        return oldValue;//返回老值
    }
//這個方法被final標記了,說明是一個本地方法,我們從openjdk裏面把相應的c語言代碼拿出來,主要要記住,下面的compareAndSwapInt方法是一個原子操作。
public final native boolean compareAndSwapInt(Object obj, long index, int oldValue, int newValue);
int compare_and_swap (int* reg, int oldval, int newval) 
{
  ATOMIC();
  int old_reg_val = *reg;
  if (old_reg_val == oldval) 
     *reg = newval;
  END_ATOMIC();
  return old_reg_val;
}

簡單分析上面的代碼,線程1和線程2並行修改值,一開始大家拿到的值都是55,線程1希望+1,改爲56,線程2也希望+1,但是如果在線程1先+1的情況下,線程再在55的基礎上+1,就沒有意義了,必須要在56的基礎上+1。我們逐行分析下代碼。
在這裏插入圖片描述
在這裏插入圖片描述

CAS算法缺點

CAS雖然很高效的解決了原子操作問題,但是CAS仍然存在三大問題。

  1. 循環時間長開銷很大。
  2. 只能保證一個共享變量的原子操作。
  3. ABA問題
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章