compare and swap,比較并替換
? ? 思路:三個(gè)參數(shù)酥夭,一個(gè)為當(dāng)前內(nèi)存值V,舊的預(yù)期值為A轻黑,即將更新的值為 B檐嚣。
? ? 當(dāng)且僅當(dāng)V=A時(shí)助泽,將內(nèi)存值修改為B并返回true,否則什么都不做返回false
public int a = 1;
public boolean compareAndSwapInt(int b) {
? ? if (a == 1) {
? ? ? ? a = b;
? ? ? ? return true;
? ? }
? ? return false;
}
看看AtomicInteger如何實(shí)現(xiàn)并發(fā)下的累加操作
假設(shè)線程A和線程B同時(shí)執(zhí)行g(shù)etAndAdd操作(分別跑在不同CPU上):
1. AtomicInteger里面的value原始值為3嚎京,即主內(nèi)存中AtomicInteger的value為3嗡贺,根據(jù)Java內(nèi)存模型,線程A和線程B各自持有一份value的副本鞍帝,值為3诫睬。
2. 線程A通過getIntVolatile(var1, var2)拿到value值3,這時(shí)線程A被掛起帕涌。
3. 線程B也通過getIntVolatile(var1, var2)方法獲取到value值3摄凡,運(yùn)氣好,線程B沒有被掛起蚓曼,并執(zhí)行compareAndSwapInt方法比較內(nèi)存值也為3亲澡,成功修改內(nèi)存值為2。
4. 這時(shí)線程A恢復(fù)纫版,執(zhí)行compareAndSwapInt方法比較床绪,發(fā)現(xiàn)自己手里的值(3)和內(nèi)存的值(2)不一致,說明該值已經(jīng)被其它線程提前修改過了,那只能重新來一遍了癞己。
5. 重新獲取value值膀斋,因?yàn)樽兞縱alue被volatile修飾,所以其它線程對它的修改痹雅,線程A總是能夠看到仰担,線程A繼續(xù)執(zhí)行compareAndSwapInt進(jìn)行比較替換,直到成功练慕。
intel手冊對lock前綴的說明如下:
1. 確保后續(xù)指令執(zhí)行的原子性惰匙。
在Pentium及之前的處理器中,帶有l(wèi)ock前綴的指令在執(zhí)行期間會(huì)鎖住總線铃将,使得其它處理器暫時(shí)無法通過總線訪問內(nèi)存,很顯然哑梳,這個(gè)開銷很大劲阎。在新的處理器中,Intel使用緩存鎖定來保證指令執(zhí)行的原子性鸠真,緩存鎖定將大大降低lock前綴指令的執(zhí)行開銷悯仙。
2. 禁止該指令與前面和后面的讀寫指令重排序。
3. 把寫緩沖區(qū)的所有數(shù)據(jù)刷新到內(nèi)存中吠卷。
上面的第2點(diǎn)和第3點(diǎn)所具有? ? 的內(nèi)存屏障效果锡垄,保證了CAS同時(shí)具有volatile讀和volatile寫的內(nèi)存語義。
CAS缺點(diǎn)
CAS存在一個(gè)很明顯的問題祭隔,即ABA問題货岭。
問題:如果變量V初次讀取的時(shí)候是A,并且在準(zhǔn)備賦值的時(shí)候檢查到它仍然是A疾渴,那能說明它的值沒有被其他線程修改過了嗎千贯?
如果在這段期間曾經(jīng)被改成B,然后又改回A搞坝,那CAS操作就會(huì)誤認(rèn)為它從來沒有被修改過搔谴。針對這種情況,java并發(fā)包中提供了一個(gè)帶有標(biāo)記的原子引用類AtomicStampedReference桩撮,它可以通過控制變量值的版本來保證CAS的正確性