CAS和LockSupport可以說(shuō)貫穿了java并發(fā)包(自旋鎖 + CAS + LockSupport + 內(nèi)存屏障 + 各種等待隊(duì)列)。
以openjdk7源碼為例,jdk/src/share/classes/sun/misc/Unsafe.java:863 compareAndSwapObject方法的注釋的注釋已經(jīng)說(shuō)明的很清晰了
/**
* Atomically update Java variable to x if it is currently
* holding expected.
* @return true if successful
*/
public final native boolean compareAndSwapObject(Object o, long offset,Object expected,Object x);
如果當(dāng)前對(duì)象o偏移offset所對(duì)應(yīng)的對(duì)象為expected,則把expected對(duì)象自動(dòng)更新為x,如果更新成功,返回true。此過(guò)程是保證原子性的。
對(duì)應(yīng)的jvm源碼實(shí)現(xiàn):hotspot/src/share/vm/prims/unsafe.cpp:1136:
// JSR166 ------------------------------------------------------------------
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h))
UnsafeWrapper("Unsafe_CompareAndSwapObject");
//更新后的值
oop x = JNIHandles::resolve(x_h);
//內(nèi)存期望值
oop e = JNIHandles::resolve(e_h);
//要改變的對(duì)象
oop p = JNIHandles::resolve(obj);
//獲取要更新的對(duì)象偏移地址
HeapWord* addr = (HeapWord *)index_oop_from_field_offset_long(p, offset);
if (UseCompressedOops) {
update_barrier_set_pre((narrowOop*)addr, e);
} else {
update_barrier_set_pre((oop*)addr, e);
}
//執(zhí)行CAS操作
oop res = oopDesc::atomic_compare_exchange_oop(x, addr, e);
jboolean success = (res == e);
if (success)
update_barrier_set((void*)addr, x);
return success;
UNSAFE_END
update_barrier_set_pre 和 update_barrier_set與垃圾收集有關(guān),暫時(shí)不做討論,主要看
oopDesc::atomic_compare_exchange_oop(x, addr, e);
hotspot/src/share/vm/oops/oop.inline.hpp:300
inline oop oopDesc::atomic_compare_exchange_oop(oop exchange_value,
volatile HeapWord *dest,
oop compare_value) {
if (UseCompressedOops) {
// encode exchange and compare value from oop to T
narrowOop val = encode_heap_oop(exchange_value);
narrowOop cmp = encode_heap_oop(compare_value);
narrowOop old = (narrowOop) Atomic::cmpxchg(val, (narrowOop*)dest, cmp);
// decode old from T to oop
return decode_heap_oop(old);
} else {
return (oop)Atomic::cmpxchg_ptr(exchange_value, (oop*)dest, compare_value);
}
}
在linux平臺(tái)下,會(huì)執(zhí)行
inline jlong Atomic::cmpxchg (jlong exchange_value, volatile jlong* dest, jlong compare_value) {
bool mp = os::is_MP();
__asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)"
: "=a" (exchange_value)
: "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
: "cc", "memory");
return exchange_value;
}
其中LOCK_IF_MP的定義:
// Adding a lock prefix to an instruction on MP machine
#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
其中is_MP是os的靜態(tài)內(nèi)聯(lián)方法,實(shí)現(xiàn)在os.cpp中,判斷當(dāng)前系統(tǒng)是否是多處理器的。
asm表示后面的代碼是內(nèi)嵌匯編,volatile表示編譯器不要優(yōu)化代碼,后面的指令保持原樣。內(nèi)嵌匯編語(yǔ)的表達(dá)格式是:
__asm__(
"匯編語(yǔ)句模板"
:輸出
:輸入
:破壞描述
)
代碼中的”=a”中=表示輸出,而“a”表示eax寄存器,變量前的“r”表示任意寄存器,“cc”表示告訴編譯器該指令的執(zhí)行將影響到標(biāo)志寄存器,要每次重新讀取,而“memory”則是告訴編譯器該指令需要重新從內(nèi)存中讀取變量的最新值,而不要從緩存了的寄存器中讀取。
LOCK_IF_MP是個(gè)宏定義,包含了多條匯編指令(匯編中指令用分號(hào)或換行來(lái)分隔)。該宏的注釋寫(xiě)得很清楚,如果是在多處理器機(jī)器上則添加lock前綴,如果是單處理器則只需要執(zhí)行cmpxchgl指令。lock前綴屬于內(nèi)存屏障,它的作用是在執(zhí)行后面指令的過(guò)程中鎖總線(或者是鎖cacheline),保證一致性。后面的cmpxchgl(即Compare and Exchange,末尾的l表示操作數(shù)長(zhǎng)度為4)就是Intel x86的比較并交換匯編指令。
LOCK_IF_MP和后面的cmpxchgl指令合起來(lái)就類似如下匯編語(yǔ)句:
cmp $0, #mp //$0表示立即數(shù)0,cmp是比較指令,若mp等于0則會(huì)置標(biāo)志位ZF=1,ZF即Zero Flag
je 1f //je為跳轉(zhuǎn)指令,當(dāng)ZF=1時(shí),je des表示跳轉(zhuǎn)到des處,此處表示跳轉(zhuǎn)到標(biāo)簽1
lock //lock前綴
1: cmpxchgl %1,(%3) //1表示一個(gè)標(biāo)簽,類似goto語(yǔ)句的標(biāo)簽
原文:https://blog.csdn.net/prstaxy/article/details/51802220
cmpxchgl的詳細(xì)執(zhí)行過(guò)程:
首先,輸入是"r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp),表示compare_value存入eax寄存器,而exchange_value、dest、mp的值存入任意的通用寄存器。嵌入式匯編規(guī)定把輸出和輸入寄存器按統(tǒng)一順序編號(hào),順序是從輸出寄存器序列從左到右從上到下以“%0”開(kāi)始,分別記為%0、%1···%9。也就是說(shuō),輸出的eax是%0,輸入的exchange_value、compare_value、dest、mp分別是%1、%2、%3、%4。
因此,cmpxchgl %1,(%3)實(shí)際上表示cmpxchgl exchange_value,(dest),此處(dest)表示dest地址所存的值。需要注意的是cmpxchgl有個(gè)隱含操作數(shù)eax,其實(shí)際過(guò)程是先比較eax的值(也就是compare_value)和dest地址所存的值是否相等,如果相等則把exchange_value的值寫(xiě)入dest指向的地址。如果不相等則把dest地址所存的值存入eax中。
輸出是"=a" (exchange_value),表示把eax中存的值寫(xiě)入exchange_value變量中。
Atomic::cmpxchg這個(gè)函數(shù)最終返回值是exchange_value,也就是說(shuō),如果cmpxchgl執(zhí)行時(shí)compare_value和dest指針指向內(nèi)存值相等則會(huì)使得dest指針指向內(nèi)存值變成exchange_value,最終eax存的compare_value賦值給了exchange_value變量,即函數(shù)最終返回的值是原先的compare_value。此時(shí)Unsafe_CompareAndSwapInt的返回值(jint)(Atomic::cmpxchg(x, addr, e)) == e就是true,表明CAS成功。如果cmpxchgl執(zhí)行時(shí)compare_value和(dest)不等則會(huì)把當(dāng)前dest指針指向內(nèi)存的值寫(xiě)入eax,最終輸出時(shí)賦值給exchange_value變量作為返回值,導(dǎo)致(jint)(Atomic::cmpxchg(x, addr, e)) == e得到false,表明CAS失敗。
假設(shè)原值為old,存在ptr所執(zhí)行的位置,想寫(xiě)入新值new,那么cmpxchg實(shí)現(xiàn)的功能就是比較old和ptr指向的內(nèi)容,如果相等則ptr所指地址寫(xiě)入new,然后返回old,如果不相等則把ptr當(dāng)前所指向地址存的值返回。