天天看點

JAVA并發鎖(二) - 重入鎖&&公平性分析ReentrantLock 原理分析

ReentrantLock 原理分析

CAS與Unsafe

Unsafe

該類在 sun.misc.Unsafe 中,是一個final類型的類,是不可繼承類,同時類中大部分操作都是native方法,調用本地方法來進行硬體操作。幾個常見的方法:

// 擷取類變量在記憶體中的偏移位址
public native long staticFieldOffset(Field var1);
// 擷取執行個體變量在記憶體中的偏移位址
public native long objectFieldOffset(Field var1);
// 擷取數組的第一個元素的偏移位址
public native int arrayBaseOffset(Class<?> var1);
// 擷取數組中元素的增量位址
public native int arrayIndexScale(Class<?> var1);
// 原子操作比較交換對象的值
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
// 原子操作比較交換Integer類型的值
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
// 原子操作比較交換Long類型的值
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
           

CAS[compare and swap], 比較和交換。

在java中整個并發操作都依賴于CAS,CAS在java.util.concurrent[J.U.C]中地位很高,很多并發操作都由CAS來實作。它是一種無鎖算法,在不适用鎖的情況下,實作多線程之間變量的共享,樂觀鎖的實作的一種。

CAS有三個操作數:

1.記憶體值V

2.需要進行比較的值A,(可了解為在沒有其它線程操作時從記憶體中擷取到的值)

3.需要更新的值B

CAS比較交換原則:當且僅當A與V的值相等,才會進行更新操作(将B值寫入到記憶體中),否則會一直循環,進行比較,直到A與V值相等後退出。比較+更新 是一個原則操作。

AtomicInteger 類:

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // cas中需要比較的值A在記憶體中的偏移位置
    private static final long valueOffset;
    static {
        try {
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    // cas中需要比較的值A,采用volatile 修飾,表示線程間可見。
    private volatile int value;
    ...
}
           

AtomicInteger 類中的 final int addAndGet(int delta) ,更新 Integer值的原子操作:

public final int addAndGet(int delta) {
  return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
public final int getAndAddInt(Object var1, long var2, int var4) {
  int var5;
  do {
    var5 = this.getIntVolatile(var1, var2);
  } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}
           

e.g.

聲明:以下 T1,T2,T3 時刻是指線程之間的上下文切換時刻。

假設有三個線程Thread1,Thread2,Thread3, 開始時AtomicInteger 中的value值為3,也就是A=3, V=3。

T1時刻,三個線程都沒有作任何更新操作,隻有Thread1作讀操作。

T2時刻,Thread2需要作更新操作,需要更新的值B=4。整個原子操作過程,首先會将A值與記憶體V值進行比較,發現A=V,那麼接下來會将B值寫入到記憶體中,即V=4,那麼此時期望比較的值A也等于4。

T3時刻,Thread1也需要進行更新操作,但是發現期望值A!=記憶體值V,那麼不會進行更新操作,此時Thread1會循環進行比較,直到A=V時,将值B寫入到記憶體中,或者經過一段時間傳回false。

T4時刻,Thread3進行讀操作,不做任何處理,直接将記憶體值V傳回即可。

整個操作過程如下圖所示:

JAVA并發鎖(二) - 重入鎖&amp;&amp;公平性分析ReentrantLock 原理分析

CAS 問題

  • ABA問題

    一個線程在更新之前會進行比較,這沒有任何問題,但是在比較之前,如果其它線程将記憶體值從原來的V=3變成V=4,再變成V=3,那麼在需要更新操作的線程中,比較A與V值時,發現沒有任何變化,即A==V,那麼需要更新的線程會誤以為沒有更新過,會直接将資料B寫入到記憶體中,但是在記憶體中确實V值進行過更新。

可通過對記憶體值V進行版本标記的方式解決。

  • 可能造成無限循環,耗時耗記憶體

    在比較A與V時,如果A與V一直不相等,那麼cas會無限循環的去比較,直到A==V時,才會退出cas,進行線程的其它操作。造成長時間的消耗記憶體。

繼續閱讀