CAS ABA問題

java.util.concurrent包的最底層基礎CAS技術,原理很簡單。

CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什麼都不做。

引發ABA問題:如果變量V初次讀取的時候是A,並且在準備賦值的時候檢查到它仍然是A,那能說明它的值沒有被其他線程修改過了嗎?如果在這段期間它的值曾經被改成了B,然後又改回A,那CAS操作就會誤認為它從來沒有被修改過。

舉例:

線程1準備用CAS將變量的值由A替換為B,在此之前,線程2將變量的值由A替換為C,又由C替換為A,然後線程1執行CAS時發現變量的值仍然為A,所以CAS成功。但實際上這時的現場已經和最初不同了,儘管CAS成功,但可能存在潛藏的問題,例如下面的例子:

CAS ABA問題


現有一個用單向鏈表實現的堆棧,棧頂為A,這時線程T1已經知道A.next為B,然後希望用CAS將棧頂替換為B:

head.compareAndSet(A,B);

在T1執行上面這條指令之前,線程T2介入,將A、B出棧,再pushD、C、A,此時堆棧結構如下圖,而對象B此時處於遊離狀態:

CAS ABA問題


此時輪到線程T1執行CAS操作,檢測發現棧頂仍為A,所以CAS成功,棧頂變為B,但實際上B.next為null,所以此時的情況變為:

CAS ABA問題


其中堆棧中只有B一個元素,C和D組成的鏈表不再存在於堆棧中,平白無故就把C、D丟掉了。

解決辦法:針對這種情況,java併發包中提供了一個帶有標記的原子引用類"AtomicStampedReference"(版本戳),它可以通過控制變量值的版本來保證CAS的正確性。


分享到:


相關文章: