結論: atomic比volatile靠譜java
java.util.concurrent.atomic.Atomic*原子類和volatile關鍵字是java中兩種常見的處理多線程下數據共享讀寫的機制。兩者看似相同,可是在實際應用中有着不小的差異。程序員
volatile關鍵字是經過本地代碼實現的寫鎖,只保證知有一個線程在寫某個數據。JVM爲了提升數據存取的速度,容許每一個線程在本身獨立的數據塊,對進程中共享的數據進行私有拷貝。volatile就是保證每次讀數據時,讀的都是存在共享數據塊裏的數據,而不是私有拷貝。然而,這種機制在有些狀況下並不安全。當兩個線程T1,T2同時對volatitle int i;做i++;時,可能出現問題。i++至關於爲i=i+1。安全
T1 LOAD i多線程
T2 LOAD i函數
T1 STORE i+1this
T2 STORE i+1atom
這裏應該執行兩次i=i+1,獲得i=i+2的,可是結果確實i=i+1。線程
所以,這邊就有了Atomic原子類存在的價值了。Atomic類被設計來解決這個問題。設計
以AtomicInteger爲例,public class AtomicInteger extends Number implements java.io.Serializable。指針
它的父類Number沒有一個Field,直接繼承與Object,方法也只有abstract的int intValue(),long longValue(),float floatValue(),double doubleValue(),和經過intValue()實現的byte byteValue(),short shortValue()。簡單的說,除了序列化外啥field都沒有都沒有。
AtomicInteger有四個field,除了序列化那個無視掉外,還剩三個。
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
valueOffset只在這個靜態塊裏被寫過,從函數命名上猜想應該是獲取這個類中名爲value的field到這個object的頭指針的偏移(offset)。這邊用到了unsafe。
Unsafe是幹什麼的?sun.misc.Unsafe,未開源。這個名字讓我想起了C#裏的關鍵字unsafe。C#爲了方便C++程序員轉C#,方便使用COM組件,給出了不安全代碼unsafe關鍵字。Java這個Unsafe,能的到一個field距離它Object頭的偏移,對Java來講顯然是不安全的操做,他給用戶直接操做內存的可能。
除了objectFieldOffset(Field)外,這個類中還使用到Unsafe的putOrderedInt(Object,long,int)和compareAndSwapInt(Object,long,int,int)。從方法名和參數看,功能分別爲向Object偏移long位置,寫入有序整型int,和比較兩值是否相同,並填入新值。
這邊有篇IBM關於compareAndSwapInt的文檔,我寫得差很少了纔看到,怨念。。。http://www.ibm.com/developerworks/cn/java/j-jtp11234/
順帶說一句,調用到compareAndSwapInt的兩個方法compareAndSet和weakCompareAndSet的在1.6裏源碼是同樣的,樣的,的。。。冗餘代碼很好玩嗎,API上寫得各類看不懂。。。
剩下一個field就是private volatile int value了。也用了volatitle,由此能夠得出,Atomic類在效率上是低於直接用volatitle,能使用volatitle是,就別用Atomic了。
而後看看AtomicInteger的method吧
從Number父類繼承來的那幾個能夠看做getter函數的value類,清一色的調用了get()方法,而後轉了一下類型。
public final int get() {
return value;
}
直接把value扔回去了,可見Atomic類在處理讀操做時和volatitle沒啥區別。
看上去像setter函數的一共有五個:
public final void set(int newValue) {
value = newValue;
}
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}
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);
}
weakCompareAndSet和compareAndSet實現上沒有區別,因此無視掉吧,反正AtomicInteger本身也只用compareAndSet。。。
set(int)和直接用volatitle沒啥區別,因此只用set做寫差很少就和徹底沒有用到Atomic類同樣,還多調用一次函數。
lazySet(int)調用了Unsafe.putOrderedInt,咱們只能猜想是直接給內存把個數字寫上去的。這樣的話其實。。。我也沒以爲有啥區別,就是告訴你們一下,你用Unsafe能夠把Java當C++寫嘛。。。
getAndSet(int)調用了compareAndSet(int,int),所做的基本就是不停的刷,看看本身改爲了沒有,改爲了救過,沒改爲繼續改。這邊要出現兩個線程不停對改怎麼辦。。。
最後一個compareAndSet(int,int)纔是實現Atomic類功能的地方。就是比一下預想的和實際的是否是同樣,同樣就就用新值蓋掉他,不同就返回個false。怎麼用呢,看看其餘函數就是到了。
對比getAndIncrement()和incrementAndGet()
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}