CAS(比較並交換)詳解

一、什麼是CAS?

CAS(Compare And Swap),就是比較並交換,是解決多線程情況下,解決使用鎖造成性能損耗問題的一種機制。

CAS包含三個操作數:

  • 變量內存位置(V)
  • 預期的變量原值(A)
  • 變量的新值(B)

當要對變量進行修改時,先會將內存位置的值與預期的變量原值進行比較,如果一致則將內存位置更新爲新值,否則不做操作,無論哪種情況都會返回內存位置當前的值。

二、CAS的實踐案例

整個JUC都是建立在CAS之上的,我們以Java8爲例,看看AtomicInteger的CAS實現:

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    /**
     * Unsafe類是提供了native方法,用於完成硬件級別的原子性操作
     * 在這裏提供Unsafe.compareAndSwapInt方法來完成CAS的比較並更新
     */
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    /**
     * value在內存中的偏移量,是即CAS中的內存地址V
     */
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;
    
    // 省略一大串代碼

    /**
     * 通過unsafe類提供的native方法完成CAS操作
     *
     * @param expect 預期的變量原值(A)
     * @param update 變量的新值(B)
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    /**
     * 進行CAS操作,自旋直到數據更新成功
     *
     * @param updateFunction a side-effect-free function
     * @return 更新後的值
     * @since 1.8
     */
    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }
}

compareAndSet的CAS實現:通過Unsafe的方法,調用native方法完成了硬件級別的原子性操作。

注意,Unsafe方法可能會在未來的jdk版本中移除,而且使用一旦出現問題可能就是JVM實例崩潰級別的問題,所以官方不推薦在應用代碼中使用。

三、CAS存在的問題

ABA問題:CAS在檢查值的時候,只會比較預期值A與內存位置的值是否相同,如果內存位置值,經過若干次修改又變回了A ( A -> B -> A),CAS檢查依舊會通過,但是實際上這個值已經修改過了。

解決方案:解決的思路就是引入類似樂觀鎖的版本號控制,不止比較預期值和內存位置的值,還要比較版本號是否正確。

實現案例:從JDK5開始,atomic包就提供了AtomicStampedReference類來解決ABA問題,相較CAS引入了一個標誌,在比較完預期值與內存地址值之後,再對預期標誌和現有標誌做比較,都通過才執行更新操作。

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