補檔CAS中的ABA問題。java
要特別注意,常見的ABA問題有兩種,要求能分別舉例解釋。node
CAS的使用可參考:git
在CAS算法中,須要取出內存中某時刻的數據(由用戶完成),在下一時刻比較並替換(由CPU完成,該操做是原子的)。這個時間差中,會致使數據的變化。程序員
假設以下事件序列:github
儘管線程 1 的CAS操做成功,但不表明這個過程沒有問題——對於線程 1 ,線程 2 的修改已經丟失。算法
在沒有垃圾回收機制的內存模型中(如C++),程序員可隨意釋放內存。併發
假設以下事件序列:spa
這裏比問題 1.1 的後果更嚴重,實際內容已經被修改了,但_線程 1 沒法感知到線程 2 的修改_。線程
更甚,若是線程 2 只釋放了A指向的內存,而線程 1 在 CAS以前還要訪問A中的內容,那麼線程 1 將訪問到一個野指針
。指針
若是位置V存儲的是鏈表的頭結點,那麼發生ABA問題的鏈表中,原頭結點是node1,線程 2 操做頭結點變化了兩次,極可能是先修改頭結點爲node2,再將node1(在C++中,也但是從新分配的節點node3,但剛好其指針等於已經釋放掉的node1)插入表頭成爲新的頭結點。
對於線程 1 ,頭結點仍舊爲 node1(或者說頭結點的值,由於在C++中,雖然地址相同,但其內容可能變爲了node3),CAS操做成功,但頭結點以後的子鏈表的狀態已不可預知。
腦補示意圖。。
問題定義已闡述清楚。
Java的垃圾回收機制已經幫咱們解決了問題 1.2;至於問題 1.1,加入版本號便可解決。
除了對象值,AtomicStampedReference內部還維護了一個「狀態戳
」。狀態戳可類比爲時間戳,是一個整數值,每一次修改對象值的同時,也要修改狀態戳,從而區分相同對象值的不一樣狀態。當AtomicStampedReference設置對象值時,對象值以及狀態戳都必須知足指望值,寫入纔會成功。
AtomicStampedReference的幾個API在AtomicReference的基礎上新增了有關時間戳的信息:
//比較設置 參數依次爲:指望值 寫入新值 指望時間戳 新時間戳
public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) //得到當前對象引用 public V getReference() //得到當前時間戳 public int getStamp() //設置當前對象引用和時間戳 public void set(V newReference, int newStamp) 複製代碼
AtomicMarkableReference和AtomicStampedReference功能類似,但AtomicMarkableReference描述更加簡單的是與否的關係。它的定義就是將狀態戳簡化爲true|false
。以下:
public final static AtomicMarkableReference<String> ATOMIC_MARKABLE_REFERENCE
= new AtomicMarkableReference<>("abc" , false);
複製代碼
操做時:
ATOMIC_MARKABLE_REFERENCE.compareAndSet("abc", "abc2", false, true);
複製代碼
本文連接:CAS中的ABA問題
做者:猴子007
出處:monkeysayhi.github.io
本文基於 知識共享署名-相同方式共享 4.0 國際許可協議發佈,歡迎轉載,演繹或用於商業目的,可是必須保留本文的署名及連接。