1. 程式人生 > >JDK併發包溫故知新系列(四)—— CAS原理與JDK8的優化

JDK併發包溫故知新系列(四)—— CAS原理與JDK8的優化

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

底層通過sun.misc.Unsafe的本地方法compareAndSwapInt實現,這個方法是原子的。

與synchronized的對比

  • 樂觀鎖與悲觀鎖的區別
  • 效能對比

synchronized是阻塞的,CAS更新是非阻塞的,只是會重試,不會有執行緒上下文切換開銷,對於大部分比較簡單的操作,無論是在低併發還是高併發情況下,這種樂觀非阻塞方式的效能都要遠高於悲觀阻塞式方式。

應用場景

  • 用來實現樂觀非阻塞演算法,確保當前執行緒方法體內使用的共享變數不被其他執行緒改變,CAS廣泛運用在非阻塞容器中。
  • 用來實現悲觀阻塞式演算法,其用在了顯式鎖的原理實現,如可重入計數中,呼叫lock()方法時將原子變數設為1,返回false的話就一直迴圈不退出,呼叫unlock將原子變數設為0。如果同時多個執行緒呼叫Lock方法那麼必然會導致原子修改不成功,保證了鎖的機制,排他性。

可能存在的問題

  • ABA問題,普通的CAS操作並不是原子的,因為有可能另一個執行緒改了值但是又改回了值,那麼樂觀鎖的方式是不能保證原子性的,若業務需要規避這種情況那麼可以使用AtomicStampedReference的compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
    方法,只有值和時間戳都相等的時候才進行原子更新,每次更新都把當前時間修改進原子變數。

JDK8的優化

JAVA8新增了LongAdder、DoubleAdder對原子變數進行進一步優化,主要是利用了分段CAS的機制,如果不用LongAdder,用AtomicLong的話,在高併發情況下,會產生一直自旋,導致效率不高。他將一個數分成若干個數,CompareAndSet方法的引數只是比較的這若干個數中的一個數,從而降低了自旋的概率