美團(tuán)終面:你確定CAS不加鎖嗎?
CAS大家都知道,這是一項(xiàng)樂(lè)觀鎖技術(shù),是Compare And Swap的簡(jiǎn)稱,顧名思義就是先比較再替換。
雖然他叫樂(lè)觀鎖,但是我們都知道它是不需要加鎖的,在JDK1.5 中的JUC就是建立在CAS之上的。相對(duì)于synchronized這種阻塞算法,CAS是非阻塞算法的一種常見(jiàn)實(shí)現(xiàn)。所以J.U.C在性能上有了很大的提升。
我們以java.util.concurrent中的AtomicInteger為例,看一下在不使用鎖的情況下是如何保證線程安全的。主要理解getAndIncrement方法,該方法的作用相當(dāng)于 ++i 操作:
getAndIncrement采用了CAS操作,每次從內(nèi)存中讀取數(shù)據(jù)然后將此數(shù)據(jù)和+1后的結(jié)果進(jìn)行CAS操作,如果成功就返回結(jié)果,否則重試直到成功為止。而compareAndSet利用unsafe的compareAndSwapInt方法實(shí)現(xiàn)的。
啥是Unsafe呢?
Unsafe是CAS的核心類。因?yàn)镴ava無(wú)法直接訪問(wèn)底層操作系統(tǒng),而是通過(guò)本地(native)方法來(lái)訪問(wèn)。不過(guò)盡管如此,JVM還是開(kāi)了一個(gè)后門,JDK中有一個(gè)類Unsafe,它提供了硬件級(jí)別的原子操作。
Unsafe是Java中一個(gè)底層類,包含了很多基礎(chǔ)的操作,比如數(shù)組操作、對(duì)象操作、內(nèi)存操作、CAS操作、線程(park)操作、柵欄(Fence)操作,JUC包、一些三方框架都使用Unsafe類來(lái)保證并發(fā)安全。
Unsafe類提供了硬件級(jí)別的原子操作,如CAS原子操作。
?但是,大家有沒(méi)有想過(guò)這樣的問(wèn)題:
硬件層面CAS又是如何保證原子性的呢?真的完全沒(méi)加鎖嗎?
拿比較常見(jiàn)的x86架構(gòu)的CPU來(lái)說(shuō),其實(shí) CAS 操作通常使用 cmpxchg 指令實(shí)現(xiàn)的。
可是為啥cmpxchg 指令能保證原子性呢?主要是有以下幾個(gè)方面的保障:
1. cmpxchg 指令是一條原子指令。在 CPU 執(zhí)行 cmpxchg 指令時(shí),處理器會(huì)自動(dòng)鎖定總線,防止其他 CPU 訪問(wèn)共享變量,然后執(zhí)行比較和交換操作,最后釋放總線。
2. cmpxchg 指令在執(zhí)行期間,CPU 會(huì)自動(dòng)禁止中斷。這樣可以確保 CAS 操作的原子性,避免中斷或其他干擾對(duì)操作的影響。
3. cmpxchg 指令是硬件實(shí)現(xiàn)的,可以保證其原子性和正確性。CPU 中的硬件電路確保了 cmpxchg 指令的正確執(zhí)行,以及對(duì)共享變量的訪問(wèn)是原子的。
所以,在操作系統(tǒng)層面,CAS還是會(huì)加鎖的,通過(guò)加鎖的方式鎖定總線,避免其他CPU訪問(wèn)共享變量。
所以,解決并發(fā)問(wèn)題,歸根結(jié)底還得靠鎖!