從相對簡單的Atomic入手(java.util.concurrent是基于Queue的并發包,而Queue,很多情況下使用到了Atomic操作,因此首先從這里開始)。很多情況下我們只是需要一個簡單的、高效的、線程安全的遞增遞減方案。注意,這里有三個條件:簡單,意味著程序員盡可能少的操作底層或者實現起來要比較容易;高效意味著耗用資源要少,程序處理速度要快;線程安全也非常重要,這個在多線程下能保證數據的正確性。這三個條件看起來比較簡單,但是實現起來卻難以令人滿意。
通常情況下,在Java里面,++i或者--i不是線程安全的,這里面有三個獨立的操作:或者變量當前值,為該值+1/-1,然后寫回新的值。在沒有額外資源可以利用的情況下,只能使用加鎖才能保證讀-改-寫這三個操作時“原子性”的。
Doug Lea在未將backport-util-concurrent合并到JSR 166里面來之前,是采用純Java實現的,于是不可避免的采用了synchronized關鍵字。
public final synchronized void set(int newValue);
public final synchronized int getAndSet(int newValue);
public final synchronized int incrementAndGet();
同時在變量上使用了volatile (后面會具體來講volatile到底是個什么東東)來保證get()的時候不用加鎖。盡管synchronized的代價還是很高的,但是在沒有JNI的手段下純Java語言還是不能實現此操作的。
JSR 166提上日程后,backport-util-concurrent就合并到JDK 5.0里面了,在這里面重復使用了現代CPU的特性來降低鎖的消耗。后本章的最后小結中會談到這些原理和特性。在此之前先看看API的使用。
一切從java.util.concurrent.atomic.AtomicInteger開始。
int addAndGet(int delta)
以原子方式將給定值與當前值相加。 實際上就是等于線程安全版本的i =i+delta操作。
boolean compareAndSet(int expect, int update)
如果當前值 == 預期值,則以原子方式將該值設置為給定的更新值。 如果成功就返回true,否則返回false,并且不修改原值。
int decrementAndGet()
以原子方式將當前值減 1。 相當于線程安全版本的--i操作。
int get()
獲取當前值。
int getAndAdd(int delta)
以原子方式將給定值與當前值相加。 相當于線程安全版本的t=i;i+=delta;return t;操作。
int getAndDecrement()
以原子方式將當前值減 1。 相當于線程安全版本的i--操作。
int getAndIncrement()
以原子方式將當前值加 1。 相當于線程安全版本的i++操作。
int getAndSet(int newValue)
以原子方式設置為給定值,并返回舊值。 相當于線程安全版本的t=i;i=newValue;return t;操作。
int incrementAndGet()
以原子方式將當前值加 1。 相當于線程安全版本的++i操作。
void lazySet(int newValue)
最后設置為給定值。 延時設置變量值,這個等價于set()方法,但是由于字段是volatile類型的,因此次字段的修改會比普通字段(非volatile字段)有稍微的性能延時(盡管可以忽略),所以如果不是想立即讀取設置的新值,允許在“后臺”修改值,那么此方法就很有用。如果還是難以理解,這里就類似于啟動一個后臺線程如執行修改新值的任務,原線程就不等待修改結果立即返回(這種解釋其實是不正確的,但是可以這么理解)。
void set(int newValue)
設置為給定值。 直接修改原始值,也就是i=newValue操作。
boolean weakCompareAndSet(int expect, int update)
如果當前值 == 預期值,則以原子方式將該設置為給定的更新值。JSR規范中說:以原子方式讀取和有條件地寫入變量但不 創建任何 happen-before 排序,因此不提供與除 weakCompareAndSet 目標外任何變量以前或后續讀取或寫入操作有關的任何保證。大意就是說調用weakCompareAndSet時并不能保證不存在happen-before的發生(也就是可能存在指令重排序導致此操作失敗)。但是從Java源碼來看,其實此方法并沒有實現JSR規范的要求,最后效果和compareAndSet是等效的,都調用了unsafe.compareAndSwapInt()完成操作。
下面的代碼是一個測試樣例,為了省事就寫在一個方法里面來了。














































由于這里例子比較簡單,這里就不做過多介紹了。
AtomicInteger和AtomicLong、AtomicBoolean、AtomicReference差不多,這里就不介紹了。在下一篇中就介紹下數組、字段等其他方面的原子操作。
參考資料:
(1)http://stackoverflow.com/questions/2443239/java-atomicinteger-what-are-the-differences-between-compareandset-and-weakcompar
(2)http://stackoverflow.com/questions/1468007/atomicinteger-lazyset-and-set