最近面試被問到一個問題,AtomicInteger如何保證線程安全?我查閱了資料 發現還可以引申到 樂觀鎖/悲觀鎖的概念,覺得值得一記。
衆所周知,JDK提供了AtomicInteger保證對數字的操作是線程安全的,線程安全我首先想到了synchronized和Lock,但是這種方式又有一個名字,叫做互斥鎖,一次隻能有一個持有鎖的線程進入,再加上還有不同線程争奪鎖這個機制,效率比較低,是以又稱“悲觀鎖”。
但是相應的有了樂觀鎖的概念,他的思路就是,它不加鎖去完成某項操作,如果因為沖突失敗就重試,直到成功為止。這種說的比較抽象,我們直接拿AtomicInteger源碼舉例,因為AtomicInteger保證線程安全就是因為使用了樂觀鎖。
Unsafe 是做一些Java語言不允許但是又十分有用的事情,具體的實作都是native方法,AtomicInteger裡調用的 Unsafe 方法 基于的是CPU 的 CAS指令來實作的。是以基于 CAS 的操作可認為是無阻塞的,一個線程的失敗或挂起不會引起其它線程也失敗或挂起。并且由于 CAS 操作是 CPU 原語,是以性能比較好。
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, );
}
CAS就是Compare and Swap的意思,比較并操作。很多的cpu直接支援CAS指令。CAS是項樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,隻有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被挂起,而是被告知這次競争中失敗,并可以再次嘗試。CAS有3個操作數,記憶體值V,舊的預期值A,要修改的新值B。當且僅當預期值A和記憶體值V相同時,将記憶體值V修改為B,否則什麼都不做。 從代碼上我們可以看到do while語句,進而證明當更新出現沖突時,即失敗時,它還會嘗試更新。符合樂觀鎖的思想。
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
最後比較一下 樂觀鎖/悲觀鎖的 差別
樂觀鎖适用于寫比較少的情況下,即沖突比較少發生,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。
但如果經常産生沖突,樂觀鎖 的重複嘗試 反倒會降低了性能,是以這種情況下用悲觀鎖就比較合适。