1. 程式人生 > >JDK8中新增原子性操作類LongAdder

JDK8中新增原子性操作類LongAdder

LongAdder簡單介紹

LongAdder類似於AtomicLong是原子性遞增或者遞減類,AtomicLong已經通過CAS提供了非阻塞的原子性操作,相比使用阻塞演算法的同步器來說效能已經很好了,但是JDK開發組並不滿足,因為在非常高的併發請求下AtomicLong的效能不能讓他們接受,雖然AtomicLong使用CAS但是CAS失敗後還是通過無限迴圈的自旋鎖不斷嘗試的


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

在高併發下N多執行緒同時去操作一個變數會造成大量執行緒CAS失敗然後處於自旋狀態,這大大浪費了cpu資源,降低了併發性。那麼既然AtomicLong效能由於過多執行緒同時去競爭一個變數的更新而降低的,那麼如果把一個變數分解為多個變數,讓同樣多的執行緒去競爭多個資源那麼效能問題不就解決了?是的,JDK8提供的LongAdder就是這個思路。下面通過圖形來標示兩者不同。

image.png image.png

如圖AtomicLong是多個執行緒同時競爭同一個變數。

image.png image.png

如圖LongAdder則是內部維護多個變數,每個變數初始化都0,在同等併發量的情況下,爭奪單個變數的執行緒量會減少這是變相的減少了爭奪共享資源的併發量,另外多個執行緒在爭奪同一個原子變數時候如果失敗並不是自旋CAS重試,而是嘗試獲取其他原子變數的鎖,最後獲取當前值時候是把所有變數的值累加後返回的。

LongAdder維護了一個延遲初始化的原子性更新陣列和一個基值變數base.陣列的大小保持是2的N次方大小,陣列表的下標使用每個執行緒的hashcode值的掩碼錶示,數組裡面的變數實體是Cell型別,Cell型別是AtomicLong的一個改進,用來減少快取的爭用,對於大多數原子操作位元組填充是浪費的,因為原子性操作都是無規律的分散在記憶體中進行的,多個原子性操作彼此之間是沒有接觸的,但是原子性陣列元素彼此相鄰存放將能經常共享快取行,所以這在效能上是一個提升。

另外由於Cells佔用記憶體是相對比較大的,所以一開始並不建立,而是在需要時候在建立,也就是惰性載入,當一開始沒有空間時候,所有的更新都是操作base變數,

自旋鎖cellsBusy用來初始化和擴容陣列表使用,這裡沒有必要用阻塞鎖,當一次執行緒發現當前下標的元素獲取鎖失敗後,會嘗試獲取其他下表的元素的鎖。更詳細的說明敬請期待 Java併發程式設計基礎之併發包原始碼剖析 一書的出版


加多

加多

高階 Java 攻城獅 at 阿里巴巴加多,目前就職於阿里巴巴,熱衷併發程式設計、ClassLoader,Spring等開源框架,分散式RPC框架dubbo,springcloud等;愛好音樂,運動。微信公眾號:技術原始積累。知識星球賬號:技術原始積累