(5)併發編程高級篇-AtomicLong、LongAdder、LongAccumlator

Java的JUC包下提供了很多的原子性操作類,都是通過非阻塞的CAS操作來實現的,相比於用鎖的方式來實現原子性操作上性能有較大的提升,本篇主要着重講解簡單的AtomicLong類以及JDK1.8新增的LongAdder、LongAccumulator類,至於其他的類都大同小異,明白其原理去理解其他的並不難。

AtomicLong類

原子性操作類的底層都是用Unsafe類進行實現的,我們看下面的AtomicLong類代碼:

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

    private volatile long value;

AtomicLong靜態代碼塊的初始化,通過Unsafe工具類獲取自身的value 內存偏移量,我們發現value 用了volatile關鍵字,是因爲要保證該變量在內存的可見性。
我們再來看AtomicLong類的幾個自增和自減的操作方法:

   /**
     * 自增當前的值
     *
     * @return 先前的值
     */
    public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }

    /**
     * 自減當前的值
     *
     * @return 先前的值
     */
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }
     /**
     * 自增當前的值
     *
     * @return 自增後的值
     */
    public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
    }

    /**
     * 自減當前的值
     * @return 自增後的值
     */
    public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
    }

如上代碼自增、自減操作都是通過unsafe的getAndLong方法實現的,此方法是原子性操作,由底層硬件CAS指令完成的。其中 第一個參數:當前AtomicLong的實例,第二個參數:value值的內存偏移地址,第三個參數:自增的值。

本源碼爲JDK1.8,此源碼和JDK1.7有所不同,但底層實現邏輯是一樣,jdk1.8 將jdk1.7中重試更新value值的源碼實現放到了getAndLong內部方法中。

雖然AtomicLong採用了非阻塞的CAS算法 來實現 原子性的操作 比 通過鎖機制來實現的性能有所提高,但是在高性能併發的情況下,它還是會存在性能問題的,即高併發的情況下會有多個線程同時競爭更新一個原子變量,更新失敗的線程會自旋進行重試。jdk1.8 提供了一個性能更高的LongAdder 類,下面我們繼續看它。

LongAdder

LongAdder 內部維護了一組cell ,每個cell 維護了一個初始值爲0的long型值。多線程併發的時候,所有線程並不是單獨操作一個cell,而是各線程分配到各cell中去。另外,當多個線程同時爭奪同一個cell失敗後,並不會就當前的cell進行自旋的CAS操作,而是分配到其他的cell進行CAS嘗試,這增加了CAS成功的可能性。當獲取值的時候,會累加所有cell變量中的value值。

LongAccumlator

它比LongAdder提供能更強大的API。

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