Java 中 CAS


一、CAS 概念

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

實現思想 CAS(V、A、B) ,V為內存地址,A為預期原值,B 為新值。如果內存地址的值與預期原址相匹配,那么將該位置值更新為新值。否則說明已經被其他線程更新,處理器不做任何處理。

無論哪種情況,它都會在 CAS 指令之前返回該位置的值。而我們可以使用自旋鎖,循環CAS ,重新讀取該變量再次嘗試修改該變量,也可以放棄操作

 

 CAS操作由處理器提供支持,是一種原語。原語是操作系統或計算機網絡用語范疇。是由若干條指令組成的,用於完成一定功能的一個過程,具有不可分割性,即原語的執行必須是連續的,在執行過程中不允許被中斷。如 Intel 處理器,比較並交換通過指令的 cmpxchg 系列實現。處理器相關指令不做過多介紹,有興趣的可自行查閱資料。

二、JDK1.8 中的CAS

Unsafe類,在sun.misc包下,不屬於Java標准。Unsafe類提供一系列增加Java語言能力的操作,如內存管理、操作類/對象/變量、多線程同步等。其中與CAS相關的方法有以下幾個:

//var1為CAS操作的對象,offset為var1某個屬性的地址偏移值,expected為期望值,var2為要設置的值,利用JNI來完成CPU指令的操作
public final native boolean compareAndSwapObject(Object var1, long offset, Object expected, Object var2);
public final native boolean compareAndSwapInt(Object var1, long offset, int expected, int var2);
public final native boolean compareAndSwapLong(Object var1, long offset, long expected, long var2);
/** 如果CAS成功,return oldValue, oldValue =  oldValue + addValue
     *  如果CAS失敗,自旋,一直運行,直到成功為止
     */
    public final Xxx getAndAddXxx(Object var1, long offset, long addValue) {
        int oldValue;
        do {
            oldValue = this.getIntVolatile(var1, offset);
        } while(!this.compareAndSwapInt(var1, offset, oldValue, oldValue + addValue));

        return oldValue;
    }

    /** 如果CAS成功,return oldValue, oldValue =  newValue
     *  如果CAS失敗,自旋,一直運行,直到成功為止
     */
    public final Xxx getAndSetXxx(Object var1, long offset, Object newValue) {
        int oldValue;
        do {
            oldValue = this.getXxxVolatile(var1, offset);
        } while(!this.compareAndSwapXxx(var1, offset, oldValue, newValue));

        return oldValue;
    }

 一般不建議使用Unsafe類,除非對它有很深入的了解。

  

  java.util.concurrent包中大量使用了CAS原理,如AtomicInteger類,都是調用上面幾個Unsafe方法保證多線程數據的正確性

  以下是AtomicInteger的CAS操作相關源碼

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

    // setup to use Unsafe.compareAndSwapInt for updates
    // Unsafe類,提供一系列增強Java的功能,如內存管理、操作類/對象/變量、多線程同步等。不建議開發者調用
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // 獲取對象某個屬性的地址偏移值
    private static final long valueOffset;

    static {
        try {
            // value相對“起始地址”的偏移量
            valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    // value值, volatile修飾,保證不同線程間的可見性
    private volatile int value;
    public AtomicInteger(int initialValue) { value = initialValue; }
    public AtomicInteger() {}

    public final int get() { return value; }
    public final void set(int newValue) { value = newValue; }

    /**
     * Eventually sets to the given value.
     *
     * @param newValue the new value
     * @since 1.6
     */
    public final void lazySet(int newValue) {
        //有序或者有延遲的putIntVolatile方法
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    /**
     * Atomically sets to the given value and returns the old value.
     * @param newValue the new value
     * @return the previous value
     */
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     * @param expect the expected value
     * @param update the new value
     * @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) {
        // JNI調用,實現CAS
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    /**
     * i++ 操作
     * Atomically increments by one the current value.
     * @return the previous value
     */
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    /**
     * i-- 操作
     * Atomically decrements by one the current value.
     * @return the previous value
     */
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }

    /**
     * return i, i = i + n 操作
     * Atomically adds the given value to the current value.
     * @param delta the value to add
     * @return the previous value
     */
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    /**
     * ++i 操作
     * Atomically increments by one the current value.
     * @return the updated value
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    /**
     * --i 操作
     * Atomically decrements by one the current value.
     * @return the updated value
     */
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }

    /**
     * i = i + n ,return i操作
     * Atomically adds the given value to the current value.
     * @param delta the value to add
     * @return the updated value
     */
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }
    // 其余函數,略...

三、CAS缺點

 CAS有幾個缺點:

  1、ABA問題。當第一個線程執行CAS操作,尚未修改為新值之前,內存中的值已經被其他線程連續修改了兩次,使得變量值經歷 A -> B -> A的過程。

  解決方案:添加版本號作為標識,每次修改變量值時,對應增加版本號; 做CAS操作前需要校驗版本號。JDK1.5之后,新增AtomicStampedReference類來處理這種情況。

  2、循環時間長開銷大。如果有很多個線程並發,CAS自旋可能會長時間不成功,會增大CPU的執行開銷。

  3、只能對一個變量進原子操作。JDK1.5之后,新增AtomicReference類來處理這種情況,可以將多個變量放到一個對象中。

參考:https://www.cnblogs.com/Shuuichi/p/10590710.html


免責聲明!

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



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