在多線程編程中,對某一資源的同步操作是保證資源狀態一致性的關鍵。這個需要同步的資源可以是單個簡單的變量,也可以是多個變量,或者是某些外部資源。對他們同步操作的含義就是同一時間點,最多只能有一個線程在操作這些資源,也就是排他性。並且一系列操作必須一氣呵成,中間不允許其他線程做相關的操作,這就是原子性。所以我們在程序中需要讓一段代碼,在同一時間最多只能有一個線程執行。在Java里我們有Synchronized關鍵字來標注一段代碼在任意時刻只能最多有一個線程在執行。在新的Java Concurrent包里邊提供了Lock相關的類。通過Lock我們可以實現與Synchronized同樣的效果。這段代碼通常我們叫它臨界區(Critical Section)。
但是Lock和Synchronize是怎么實現的呢?
從代碼上看起來好像在某個地方有個開關。一個線程執行到臨界區時,檢查這個開關,如果是0代表可以進入,然后把開關設成1,進入,退出,把開關設成0。看起來很完美?其實沒有那么簡單。因為你這個開關本身可能被多個線程同時檢查,它們同時檢查到了0,同時進入。。啊 這可不好。再弄個開關?好像陷入無窮無盡的開關也解決不了問題。更天才的想法是,把自己的線程號賦予這個變量,然后等待一小段時間,再檢查這個變量是不是自己的進程號,如果是自己,恭喜搶到了。等一小段時間是為了防止其它線程同時檢查了,並在你檢查之后寫了它的線程號。但是,這個等待時間可能很難確定,CPU的線程調度讓以上線程間的操作無序且無法知道。看看,這個問題真的貌似不好通過簡單的程序詭計解決啊。也許你可以想出更天才的純軟件解決方案,但它可能不那么可靠或者高效。
軟件解決不了的問題,其實利用硬件很容易解決。大多CPU都提供了原子操作。請參考開頭我提到的文檔。CPU保證了某些對單個變量的比較和交換操作是原子的。也就是比較某個數是不是你預期的,如果是,賦予你給的變量,否則不做操作。可以想象CPU在執行此類指令時,可能要暫停多核情況下其它核心的執行,霸占總線,讓這個操作不被打斷-除非一個CPU周期就可以做完,並且可能要清理CPU的緩存。。。 但是這些還是比我們上邊提到的純軟件方法快速且靠譜。
好了,回頭看看我們的Java怎么用到CMPXCHG的。
首先進入Java世界。請看ReentrantLock$FairSync.tryAcquire。有沒有看到一句話跟我們說的CMPXCHG是同義詞?對了,就是compareAndSetState(0, acquires)。一路着下去。。 到了Unsafe.compareAndSwapInt(針對Sun/Oracle Java).這個方法是native的-我們到達了Java世界的邊緣。
然后,讓我們進入C++的世界。請找到OpenJDK的unsafe.cpp。找到:
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper(“Unsafe_CompareAndSwapInt”);
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
是不是找到了atomic.cpp。你看到了很多平台判斷的預處理指令。啊,神奇的預處理,C語言實現跨平台的法寶。看起來到此為止,我們也到達了C/C++世界的邊緣。
最后,匯編語言-CPU指令的等價符號。找個比較親民的平台的把,x86下的atomic_linux_x86.inline.hpp。是的HPP,這里還不是.S。因為C/C++跟匯編的世界交界處不像到Java的世界那樣有一堵不透風的牆。匯編可以自由的嵌入C/C++。到此我們終於找到了CPU指令了。
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
int mp = os::is_MP();
__asm__ volatile (LOCK_IF_MP(%4) “cmpxchgl %1,(%3)”
: “=a” (exchange_value)
: “r” (exchange_value), “a” (compare_value), “r” (dest), “r” (mp)
: “cc”, “memory”);
return exchange_value;
}
接下來還有CPU微指令,邏輯電路,這里大概就是軟件和硬件世界的邊緣了。
