之前講鎖的實現(xiàn)的時候講到了CAS魔熏,但沒有詳細的講述CAS是什么鸽扁,只是說明了CAS能保證原子性,那么原子性是什么滓窍?CAS到底又是什么呢巩那?
對于原子性、可見性即横、有序性可以自行學習,如果有時間跺嗽,可以單開一篇講述。
在講CAS之前桨嫁,我們先來想想,在多線程下璃吧,Java如何保證同步,首先會想到的就是synchronized關(guān)鍵字
但是眾所周知筒繁,synchronized是一個重量級的關(guān)鍵字巴元,它會導致有鎖,導致性能不高逮刨。
而volatile只能保證可見性,并不能保證原子性恢总。(volatile的相關(guān)知識可自行學習)
那么為了保證同步箩退,鎖機制是必然需要的离熏。
synchronized是一種獨占鎖戴涝,也是一種悲觀鎖钻蔑,他相信一定有線程去修改他,那么他要讓其他線程掛起等待可帽,直到鎖被釋放窗怒,這樣的效率是較低的。
那么相對的扬虚,樂觀鎖的效率會更高一些,所謂樂觀鎖荸镊,就是相信沒有其他線程去修改他(如果修改失敗了,繼續(xù)重試躬存,直到成功)。
CAS:Compare And Swap
CAS指的是現(xiàn)代CPU廣泛支持的一種對內(nèi)存中的共享數(shù)據(jù)進行操作的特殊指令宛逗。
簡單講一下這個指令的操作過程:首先CPU會將內(nèi)存中需要被更改的數(shù)據(jù)與期望值進行對比钦椭。當兩個值相等時,CPU才會將值替換成新的值彪腔,否則認為更改失敗,不做任何操作德挣。這一系列的操作是原子的。
簡單的說番挺,CAS指令有三個參數(shù)屯掖,內(nèi)存值V,舊的預期值A贴铜,新的修改的值B,當且僅當內(nèi)存值V和舊的預期值A相同時徘意,內(nèi)存值V才會修改成B轩褐。
CAS會帶來一個著名的ABA問題
1.進程P1在共享變量中讀到值為A
2.P1被搶占了,進程P2執(zhí)行
3.P2把共享變量里的值從A改成了B把介,再改回到A,此時被P1搶占脚牍。
4.P1回來看到共享變量里的值沒有被改變,于是繼續(xù)執(zhí)行莫矗。
盡管CAS還是會成功執(zhí)行,但是這樣會帶來隱藏的問題作谚。
例如現(xiàn)在有一個隊列A->B->C,此時head=A, 通過CAS操作將A替換成B,此時進程P1在內(nèi)存的共享變量中讀到值為A
在這個時候進程P1被搶占了雀监,進程P2執(zhí)行
P2把A,B,C三個值都pop掉眨唬,再將A,C,D三個值push進來,此時隊列變成了A->C->D瓦宜,但是head仍為A,此時被P1搶占
P1回來看到共享變量里面的值沒有被改變临庇,于是繼續(xù)執(zhí)行昵慌,此時head就變成了B,此時B.next = null, 隊列里就只剩下了B斋攀,丟失了C、D兩個值侧蘸。
對于ABA問題肖方,一般的解決方案是加上版本號來區(qū)分是否真的沒有被修改過未状,在Java中AtomicStampedReference<E>也實現(xiàn)了這個作用,有興趣的可以了解一下司草,其實質(zhì)是將共享變量和版本號包裝成一個對象來進行CAS操作, 每次CAS傳入共享變量和版本號的舊值和新值,來判斷共享變量是否真的沒有被改變過猜憎。