java.util.concurrent.atomic原子操做類包

這個包裏面提供了一組原子變量類。其基本的特性就是在多線程環境下,當有多個線程同時執行這些類的實例包含的方法時,具備排他性,即當某個線程進入方法,執行其中的指令時,不會被其餘線程打斷,而別的線程就像自旋鎖同樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另外一個線程進入,這只是一種邏輯上的理解。其實是藉助硬件的相關指令來實現的,不會阻塞線程(或者說只是在硬件級別上阻塞了)。能夠對基本數據、數組中的基本數據、對類中的基本數據進行操做。原子變量類至關於一種泛化的volatile變量,可以支持原子的和有條件的讀-改-寫操做。
    java.util.concurrent.atomic中的類能夠分紅4組:
標量類(Scalar):AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
數組類:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新器類:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
複合變量類:AtomicMarkableReference,AtomicStampedReference
第一組AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference這四種基本類型用來處理布爾,整數,長整數,對象四種數據,其內部實現不是簡單的使用synchronized,而是一個更爲高效的方式CAS (compare and swap) + volatile和native方法,從而避免了synchronized的高開銷,執行效率大爲提高。如AtomicInteger的實現片段爲:
Java代碼  收藏代碼
java

private static final Unsafe unsafe = Unsafe.getUnsafe();  

private volatile int value;  

public final int get() {  

        return value;  

}  

public final void set(int newValue) {  

        value = newValue;  

}  

public final boolean compareAndSet(int expect, int update) {  

    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  

}

構造函數(兩個構造函數)
默認的構造函數:初始化的數據分別是false,0,0,null
帶參構造函數:參數爲初始化的數據
set( )和get( )方法:能夠原子地設定和獲取atomic的數據。相似於volatile,保證數據會在主存中設置或讀取
void set()和void lazySet():set設置爲給定值,直接修改原始值;lazySet延時設置變量值,這個等價於set()方法,可是因爲字段是volatile類型的,所以次字段的修改會比普通字段(非volatile字段)有稍微的性能延時(儘管能夠忽略),因此若是不是想當即讀取設置的新值,容許在「後臺」修改值,那麼此方法就頗有用。
getAndSet( )方法
原子的將變量設定爲新數據,同時返回先前的舊數據
其本質是get( )操做,而後作set( )操做。儘管這2個操做都是atomic,可是他們合併在一塊兒的時候,就不是atomic。在Java的源程序的級別上,若是不依賴synchronized的機制來完成這個工做,是不可能的。只有依靠native方法才能夠。
Java代碼  收藏代碼
數組

public final int getAndSet(int newValue) {  

    for (;;) {  

        int current = get();  

        if (compareAndSet(current, newValue))  

            return current;  

    }  

}

compareAndSet( ) 和weakCompareAndSet( )方法
這 兩個方法都是conditional modifier方法。這2個方法接受2個參數,一個是指望數據(expected),一個是新數據(new);若是atomic裏面的數據和指望數據一 致,則將新數據設定給atomic的數據,返回true,代表成功;不然就不設定,並返回false。JSR規範中說:以原子方式讀取和有條件地寫入變量但不 建立任何 happen-before 排序,所以不提供與除 weakCompareAndSet 目標外任何變量之前或後續讀取或寫入操做有關的任何保證。大意就是說調用weakCompareAndSet時並不能保證不存在happen- before的發生(也就是可能存在指令重排序致使此操做失敗)。可是從Java源碼來看,其實此方法並無實現JSR規範的要求,最後效果和 compareAndSet是等效的,都調用了unsafe.compareAndSwapInt()完成操做。
Java代碼  收藏代碼
安全

public final boolean compareAndSet(int expect, int update) {  

    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  

}  

public final boolean weakCompareAndSet(int expect, int update) {  

    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  

}

 
對於 AtomicInteger、AtomicLong還提供了一些特別的方法。
getAndIncrement( ):以原子方式將當前值加 1,至關於線程安全的i++操做。
incrementAndGet( ):以原子方式將當前值加 1, 至關於線程安全的++i操做。
getAndDecrement( ):以原子方式將當前值減 1, 至關於線程安全的i--操做。
decrementAndGet ( ):以原子方式將當前值減 1,至關於線程安全的--i操做。
addAndGet( ): 以原子方式將給定值與當前值相加, 實際上就是等於線程安全的i =i+delta操做。
getAndAdd( ):以原子方式將給定值與當前值相加, 至關於線程安全的t=i;i+=delta;return t;操做。
以實現一些加法,減法原子操做。(注意 --i、++i不是原子操做,其中包含有3個操做步驟:第一步,讀取i;第二步,加1或減1;第三步:寫回內存)
使用AtomicReference建立線程安全的堆棧
Java代碼  收藏代碼
多線程

package thread;  

import java.util.concurrent.atomic.AtomicReference;  

public class ConcurrentStack<T> {  

    private AtomicReference<Node<T>>    stacks  = new AtomicReference<Node<T>>();  

    public T push(T e) {  

        Node<T> oldNode, newNode;  

        for (;;) { // 這裏的處理很是的特別,也是必須如此的。  

            oldNode = stacks.get();  

            newNode = new Node<T>(e, oldNode);  

            if (stacks.compareAndSet(oldNode, newNode)) {  

                return e;  

            }  

        }  

    }     

    public T pop() {  

        Node<T> oldNode, newNode;  

        for (;;) {  

            oldNode = stacks.get();  

            newNode = oldNode.next;  

            if (stacks.compareAndSet(oldNode, newNode)) {  

                return oldNode.object;  

            }  

        }  

    }     

    private static final class Node<T> {  

        private T       object;       

        private Node<T>   next;         

        private Node(T object, Node<T> next) {  

            this.object = object;  

            this.next = next;  

        }  

    }     

}

    雖然原子的標量類擴展了Number類,但並無擴展一些基本類型的包裝類,如Integer或Long,事實上他們也不能擴展:基本類型的包裝類是不能夠修改的,而原子變量類是能夠修改的。在原子變量類中沒有從新定義hashCode或equals方法,每一個實例都是不一樣的,他們也不宜用作基於散列容器中的鍵值。
      第二組AtomicIntegerArray,AtomicLongArray還有AtomicReferenceArray類進一步擴展了原子操做,對這些類型的數組提供了支持。這些類在爲其數組元素提供 volatile 訪問語義方面也引人注目,這對於普通數組來講是不受支持的。
他們內部並非像AtomicInteger同樣維持一個valatile變量,而是所有由native方法實現,以下
AtomicIntegerArray的實現片段:
Java代碼  收藏代碼
app

private static final Unsafe unsafe = Unsafe.getUnsafe();  

private static final int base = unsafe.arrayBaseOffset(int[].class);  

private static final int scale = unsafe.arrayIndexScale(int[].class);  

private final int[] array;  

public final int get(int i) {  

        return unsafe.getIntVolatile(array, rawIndex(i));  

}  

public final void set(int i, int newValue) {  

        unsafe.putIntVolatile(array, rawIndex(i), newValue);  

}

 
第三組AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater基於反射的實用工具,能夠對指定類的指定 volatile 字段進行原子更新。API很是簡單,可是也是有一些約束:
(1)字段必須是volatile類型的
(2)字段的描述類型(修飾符public/protected/default/private)是與調用者與操做對象字段的關係一致。也就是說 調用者可以直接操做對象字段,那麼就能夠反射進行原子操做。可是對於父類的字段,子類是不能直接操做的,儘管子類能夠訪問父類的字段。
(3)只能是實例變量,不能是類變量,也就是說不能加static關鍵字。
(4)只能是可修改變量,不能使final變量,由於final的語義就是不可修改。實際上final的語義和volatile是有衝突的,這兩個關鍵字不能同時存在。
(5)對於AtomicIntegerFieldUpdater 和AtomicLongFieldUpdater 只能修改int/long類型的字段,不能修改其包裝類型(Integer/Long)。若是要修改包裝類型就須要使用AtomicReferenceFieldUpdater 。
 netty5.0中類ChannelOutboundBuffer統計發送的字節總數,因爲使用volatile變量已經不能知足,因此使用AtomicIntegerFieldUpdater 來實現的,看下面代碼:
Java代碼  收藏代碼
函數

//定義  

    private static final AtomicLongFieldUpdater<ChannelOutboundBuffer> TOTAL_PENDING_SIZE_UPDATER =  

            AtomicLongFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "totalPendingSize");  

  

    private volatile long totalPendingSize;  

  

  

  

//使用  

        long oldValue = totalPendingSize;  

        long newWriteBufferSize = oldValue + size;  

        while (!TOTAL_PENDING_SIZE_UPDATER.compareAndSet(this, oldValue, newWriteBufferSize)) {  

            oldValue = totalPendingSize;  

            newWriteBufferSize = oldValue + size;  

        }
相關文章
相關標籤/搜索