java如何實現原子操做CAS

在Java中能夠經過鎖和循環CAS的方式來實現原子操做。java

使用循環CAS實現原子操做

JVM中的CAS操做正是利用了處理器提供的CMPXCHG指令實現的。自旋CAS實現的基本思路就是循環進行CAS操做直到成功爲止。編程

CAS實現原子操做的三大問題

ABA問題,循環時間長開銷大,以及只能保證一個共享變量的原子操做。數組

ABA問題

由於CAS須要在操做值的時候,檢查值有沒有發生變化,若是沒有發生變化 則更新,可是若是一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它 的值沒有發生變化,可是實際上卻變化了。ABA問題的解決思路就是使用版本號。在變量前面 追加上版本號,每次變量更新的時候把版本號加1,那麼A→B→A就會變成1A→2B→3A。從 Java 1.5開始,JDK的Atomic包裏提供了一個類AtomicStampedReference來解決ABA問題。這個 類的compareAndSet方法的做用是首先檢查當前引用是否等於預期引用,而且檢查當前標誌是 否等於預期標誌,若是所有相等,則以原子方式將該引用和該標誌的值設置爲給定的更新值。併發

public boolean compareAndSet(
         V expectedReference, // 預期引用 
         V newReference, // 更新後的引用 
        int expectedStamp, // 預期標誌 
        int newStamp // 更新後的標誌 )

循環時間長開銷大

自旋CAS若是長時間不成功,會給CPU帶來很是大的執行開銷。如 果JVM能支持處理器提供的pause指令,那麼效率會有必定的提高。pause指令有兩個做用:第 一,它能夠延遲流水線執行指令(de-pipeline),使CPU不會消耗過多的執行資源,延遲的時間 取決於具體實現的版本,在一些處理器上延遲時間是零;第二,它能夠避免在退出循環的時候 因內存順序衝突(Memory Order Violation)而引發CPU流水線被清空(CPU Pipeline Flush),從而 提升CPU的執行效率。this

只能保證一個共享變量的原子操做

當對一個共享變量執行操做時,咱們可使用循 環CAS的方式來保證原子操做,可是對多個共享變量操做時,循環CAS就沒法保證操做的原子 性,這個時候就能夠用鎖。還有一個取巧的辦法,就是把多個共享變量合併成一個共享變量來 操做。好比,有兩個共享變量i=2,j=a,合併一下ij=2a,而後用CAS來操做ij。從Java 1.5開始, JDK提供了AtomicReference類來保證引用對象之間的原子性,就能夠把多個變量放在一個對 象裏來進行CAS操做。線程

使用鎖機制實現原子操做

鎖機制保證了只有得到鎖的線程纔可以操做鎖定的內存區域。JVM內部實現了不少種鎖 機制,有偏向鎖、輕量級鎖和互斥鎖。有意思的是除了偏向鎖,JVM實現鎖的方式都用了循環 CAS,即當一個線程想進入同步塊的時候使用循環CAS的方式來獲取鎖,當它退出同步塊的時 候使用循環CAS釋放鎖。code

原子操做類

原子更新基本類型

  • AtomicBoolean:原子更新布爾類
  • AtomicInteger:原子更新整型。
  • AtomicLong:原子更新長整型。

getAndIncrement是如何實現原子操做的呢?對象

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

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

源碼中for循環體的第一步先取得AtomicInteger裏存儲的數值,第二步對AtomicInteger的當 前數值進行加1操做,關鍵的第三步調用compareAndSet方法來進行原子更新操做,該方法先檢 查當前數值是否等於current,等於意味着AtomicInteger的值沒有被其餘線程修改過,則將 AtomicInteger的當前數值更新成next的值,若是不等compareAndSet方法會返回false,程序會進 入for循環從新進行compareAndSet操做。ip

原子更新數組

  • AtomicIntegerArray:原子更新整型數組裏的元素。
  • AtomicLongArray:原子更新長整型數組裏的元素。
  • AtomicReferenceArray:原子更新引用類型數組裏的元素。
  • AtomicIntegerArray類主要是提供原子的方式更新數組裏的整型

須要注意的是,數組value經過構造方法傳遞進去,而後AtomicIntegerArray會將當前數組 複製一份,因此當AtomicIntegerArray對內部的數組元素進行修改時,不會影響傳入的數組。內存

原子更新引用類型

  • AtomicReference:原子更新引用類型。
  • AtomicReferenceFieldUpdater:原子更新引用類型裏的字段。
  • AtomicMarkableReference:原子更新帶有標記位的引用類型。

原子更新字段類

  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
  • AtomicLongFieldUpdater:原子更新長整型字段的更新器。
  • AtomicStampedReference:原子更新帶有版本號的引用類型。

參考

《java併發編程的藝術》

相關文章
相關標籤/搜索