java中,可能有一些場景,操作非常簡單,但是容易存在並發問題,比如i++,
此時,如果依賴鎖機制,可能帶來性能損耗等問題,
於是,如何更加簡單的實現原子性操作,就成為java中需要面對的一個問題。
在backport-util-concurrent沒有被引入java1.5並成為JUC之前,
這些原子類和原子操作方法,都是使用synchronized實現的。
不過JUC出現之后,這些原子操作 基於JNI提供了新的實現,
比如AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference,AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray;
這些操作中提供一些原子化操作,比如incrementAndGet(相當於i++),compareAndSet(安全賦值)等,直接讀源代碼也很容易懂。
以AtomicInteger為例,看看它是怎么做到的:
如果是讀取值,很簡單,將value聲明為volatile的,就可以保證在沒有鎖的情況下,數據是線程可見的:
1 private volatile int value;
public final int get() { 2 return value; 3 }
那么,涉及到值變更的操作呢?以AtomicInteger實現:++i為例:
1 public final int incrementAndGet() { 2 for (;;) { 3 int current = get(); 4 int next = current + 1; 5 if (compareAndSet(current, next)) 6 return next; 7 } 8 }
在這里采用了CAS操作,每次從內存中讀取數據然后將此數據和+1后的結果進行CAS操作,如果成功就返回結果,否則重試直到成功為止。
而這里的comparAndSet(current,next),就是前面介紹CAS的時候所說的依賴JNI實現的樂觀鎖做法:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
數組原子化
注意,Java中Atomic*Array,並不是對整個數組對象實現原子化(也沒有必要這樣做),而是對數組中的某個元素實現原子化。
例如,對於一個整型原子數組,其中的原子方法,都是對每個元素的:
1 public final int getAndDecrement(int i) { 2 while (true) { 3 int current = get(i); 4 int next = current - 1; 5 if (compareAndSet(i, current, next)) 6 return current; 7 } 8 }
引用的原子化操作
引用的操作本身不就是原子的嗎?
一個對象的引用,從A切換到B,本身也不會出現 非原子操作啊?這種想法本身沒有什么問題,
但是考慮下嘛的場景:對象a,當前執行引用a1,
線程X期望將a的引用設置為a2,也就是a=a2,
線程Y期望將a的引用設置為a3,也就是a=a3。
X要求,a必須從a1變為a2,也就是說compareAndSet(expect=a1,setValue=a2);
Y要求,a必須從a1變為a3,也就是說compareAndSet(expect=a1,setValue=a3)。
如果嚴格遵循要求,應該出現X把a的引用設置為a2后,Y線程操作失敗的情況,也就是說:
X:a==a1--> a=a2;
Y:a!=a1 --> Exception;
如果沒有原子化,那么Y會直接將a賦值為a3,從而導致出現臟數據。
這就是原子引用AtomicReference存在的原因。
1 public final V getAndSet(V newValue) { 2 while (true) { 3 V x = get(); 4 if (compareAndSet(x, newValue)) 5 return x; 6 } 7 }
注意,AtomicReference要求引用也是volatile的。
Updater原子化
其它幾個Atomic類,都是對被volatile修飾的基本數據類型的自身數據進行原子化操作,
但是如果一個被volatile修飾的變量本身已經存在在類中,那要如何提供原子化操作呢?
比如,一個Person,其中有個屬性為age,private volatile int age,
如何對age提供原子化操作呢?
1 private AtomicIntegerFieldUpdater<Person> updater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age"); 2 updater.getAndIncrement(5);//加5歲 3 updater.compareAndSet(person, 30, 35)//如果一個人的年齡是30,設置為35。
Java中的Atomic包使用指南
參考: http://ifeve.com/java-atomic/