AtomicInteger 理論與實踐

publicclass AtomicInteger

extends Number

implements Serializable

 

可以用原子方式更新的 int 值。有關原子變量屬性的描述,請參閱 java.util.concurrent.atomic 包規範。AtomicInteger 可用在應用程序中(如以原子方式增加的計數器),並且不能用於替換 Integer。但是,此類確實擴展了 Number,允許那些處理基於數字類的工具和實用工具進行統一訪問。

 

概述:

JavaAPI的說明中:AtomicInteger可以原子的更新int值。從應用程序的角度來看,我們可以使用AtomicInteger來構建以原子方式增加的計數器。

源碼分析:

1 使用CAS進行原子更新操作。

Java併發包中大量使用了CAS操作。CAS 操作包含三個操作數 ——內存位置(V)、預期原值(A)和新值(B)。如果內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值。否則,處理器不做任何操作。

 // 定義unsafe 成員變量

private static finalUnsafe unsafe = Unsafe.getUnsafe();

 

// 取得value字段的偏移量

static {

  try {

    valueOffset = unsafe.objectFieldOffset

        (AtomicInteger.class.getDeclaredField("value"));

  } catch (Exception ex) { throw newError(ex); }

}

// 原子操作:比較並更新

public final booleancompareAndSet(int expect, int update) {

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

}

// 原子操作:比較並更新

public final booleanweakCompareAndSet(int expect, int update) {

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

}

 

Java中具體的CAS操作類是sun.misc.UnsafeUnsafe類提供了硬件級別的原子操作,Java無法直接訪問到操作系統底層(如系統硬件等),爲此Java使用native方法來擴展Java程序的功能。在OpenJDK公開的Unsafe源代碼中,可以看到Unsafe類的方法都是通過native關鍵字從而將實現委託給操作系統了。

public native longobjectFieldOffset(Field f);

 

public final native booleancompareAndSwapInt(Object o, long offset,

                                                 int expected,

                                                 int x);

 

2 使用volatile關鍵字修飾int成員變量value

privatevolatile int value;

volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內存中重讀該成員變量的值。而且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存。這樣在任何時刻,兩個不同的線程總是看到某個成員變量的值是相同的,更簡單一點理解就是volatile修飾的變量值發生變化時對於另外的線程是可見的。

關於volatile修飾符的相信信息可參考Java理論與實踐:正確使用volatile變量 一文。

注意點

對於AtomicInteger提供的公開API,大部分都一目瞭然,不用多說,有兩點值得注意。

首先是比較並交換,公開的API中提供了兩種方案:compareAndSetweakCompareAndSet。儘管規範中說:weakCompareAndSet可能會在意外的情況下失敗,並對指令重排序不提供保證,但是從實現的角度來看,這兩個方法都是調用unsafe.compareAndSwapInt(this, valueOffset, expect, update)來實現的,因此實現並沒有遵守規範。也許將來會重新實現這個方法,只是目前還缺乏合適的解決方案。

其次就是1.6新增的lazySet方法。

 

Unsafe.putOrderedObject guarante that writes will notbe re-orderd by instruction reordering. Under the covers it uses the fasterstore-store barrier, rather than the the slower store-load barrier, which isused when doing a volatile write.

write may be reordered with subsequent operations (orequivalently, might not be visible to other threads) until some other volatilewrite or synchronizing action occurs)

 

也就是說能夠保證寫寫不會被重排序,但是不保證寫會對其它線程可見,而volatile既保證寫寫不會被重排序,也保證寫後對其它線程立即可見。可見Unsafe.putOrderedObject會比直接的volatile變量賦值速度會一點,這篇文章則指出Unsafe.putOrderedObject會比volatile寫快3倍。

 

源文檔 <http://onlychoice.github.io/blog/2013/09/17/java-concurrent-source-code-reading-3/>

 

 

As probably the last little JSR166 follow-up forMustang, we added a "lazySet" method to the Atomic classes(AtomicInteger, AtomicReference, etc). This is a niche method that is sometimesuseful when fine-tuning code using non-blocking data structures. The semanticsare that the write is guaranteed not to be re-ordered with any previous write,but may be reordered with subsequent operations (or equivalently, might not bevisible to other threads) until some other volatile write or synchronizingaction occurs).

The main use case is for nulling out fields ofnodes in non-blocking data structures solely for the sake of avoiding long-termgarbage retention; it applies when it is harmless if other threads see non-nullvalues for a while, but you'd like to ensure that structures are eventuallyGCable. In such cases, you can get better performance by avoiding the costs ofthe null volatile-write. There are a few other use cases along these lines fornon-reference-based atomics as well, so the method is supported across all ofthe AtomicX classes.

For people who like to think of these operationsin terms of machine-level barriers on common multiprocessors, lazySet providesa preceeding store-store barrier (which is either a no-op or very cheap oncurrent platforms), but no store-load barrier (which is usually the expensivepart of a volatile-write).

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