CAS,即Compare and Swap,中文翻譯為“比較并交換”。
對于JUC包中,CAS理論是實(shí)現(xiàn)整個java并發(fā)包的基石。從整體來看,concurrent包的實(shí)現(xiàn)示意圖如下:

i++是一個非常經(jīng)典的操作,它幾乎充斥著我們每個人編寫的代碼中。我們知道i++是可以分解的,它分解為getI()、i + 1 、setI三個步驟,所以它并不是原子操作。如果i==1,執(zhí)行兩次i++操作,我們期望的結(jié)果是3,但是結(jié)果有可能也是2:

那么有什么辦法解決這個問題呢?肯定有!使用鎖即可:
synchronized(this){ i++; }
誠然,在java中存在樂觀鎖、悲觀鎖兩種鎖。其中synchronized就是悲觀鎖,在前面我們了解synchronized也是獨(dú)占鎖,加入關(guān)鍵字synchronized的代碼一般都是以單線程的形式在運(yùn)行著,它會導(dǎo)致其他需要該資源的線程掛起直到前面的線程執(zhí)行完畢釋放資源,所以它的效率較為低下。而樂觀鎖則采用了一種較為高效的方式,它的操作與synchronized不同,synchronized采用加鎖,而它則不采用加鎖去執(zhí)行某些操作,如果發(fā)生了沖突則失敗并一直重試直到成功為止。而CAS就是一種樂觀鎖,它所采用的策略是當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和存中的值V相同,則將內(nèi)存V值修改為B,否則返回V。實(shí)現(xiàn)如下:
for(;;){ if(A==V){ V = B; } }
當(dāng)然在J.U.C中實(shí)現(xiàn)CAS沒有這么簡單。
CAS,即一種對內(nèi)存中的共享數(shù)據(jù)進(jìn)行操作的指令,而且該操作是原子的讀寫操作。其過程如下:首先CPU將內(nèi)存中的將要被修改的數(shù)據(jù)與預(yù)期的值進(jìn)行比較,如果這兩個值相等,CPU則會將內(nèi)存中數(shù)值替換為新值,否則不做操作。最后,CPU會將舊值返回。在java中,CAS的含義就是“我認(rèn)為的原本的值是什么,如果你是,則更換為新值,否則不做修改同時麻煩告訴我該值時多少”。
在CAS中,總共存在三個操作數(shù):預(yù)期值A(chǔ)、內(nèi)存中的V、修改的值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存中的值V相同,則將內(nèi)存V值修改為B,否則返回V。使用這種機(jī)制編寫的算法也叫作非阻塞算法,標(biāo)準(zhǔn)定義了一個線程的失敗或者掛起是不會影響其他線程的失敗或者掛起。
下面我們來已AtomicIneger的源碼為例來看看CAS操作:
public final int getAndAdd(int delta) { for (;;) { int current = get(); int next = current + delta; if (compareAndSet(current, next)) return current; } }
這里很顯然使用CAS操作(for(;;)里面),他每次都從內(nèi)存中讀取數(shù)據(jù),+1操作,然后兩個值進(jìn)行CAS操作。如果成功則返回,否則失敗重試,直到修改成功為止。
上面源碼最關(guān)鍵的地方有兩個,一個for循環(huán),它代表著一種寧死不屈的精神,不成功誓不罷休。還有就是compareAndSet:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
盡管CAS機(jī)制可以使我們不依賴與同步,不影響和掛起其他線程,它大大提升了運(yùn)行的效率,但是它會導(dǎo)致一個ABA的問題,如下:加入有兩個線程A、B,他們都讀取內(nèi)存中的數(shù)據(jù)V,假如這個時候線程A,先將V修改為V1,然后又修改為V,這個時候線程B的compareAndSet仍然能成功,對于線程B而言該值V并沒有發(fā)生任何變化,而實(shí)際上它已經(jīng)變化了,只不過最后又還原了而已。
參考文獻(xiàn)
1、Java的多線程編程模型5--Java中的CAS理論
2、JAVA CAS原理深度分析
新聞熱點(diǎn)
疑難解答