CAS 是樂觀鎖設計思想的實現。CAS 的思想是:在“讀取 - 修改 - 寫回”操作序列中,先讀取並修改數據,寫回數據前先判斷讀取數據後的這段時間內數據是否發生變化(共用變數的當前值是否是我們的期望值)。通過 CAS 我們可以以無鎖的方式,保證對共用數據進行 “讀取 - 修改 - 寫回” 操作序列的... ...
介紹 CAS
技術是為瞭解決問題而生的,通過 CAS 我們可以以無鎖的方式,保證對共用數據進行 “讀取 - 修改 - 寫回” 操作序列的正確性。
CAS 是樂觀鎖設計思想的實現。CAS 的思想是:在“讀取 - 修改 - 寫回”操作序列中,先讀取並修改數據,寫回數據前先判斷讀取數據後的這段時間內數據是否發生變化(共用變數的當前值是否是我們的期望值):
- 如果在此期間數據沒有發生變化(共用資源的當前值是我們的期望值),那麼就把修改後的值寫回
- 如果在此期間其他的線程修改了數據,數據發生了變化(共用資源的當前值不是我們的期望值),那麼就放棄本次寫回操作,再基於最新的數據進行修改然後重試,避免發生數據更新丟失
CAS 更加底層的實現依賴於 CPU 提供的特定指令,具體根據體繫結構的不同還存在著明顯區別。比如,x86 CPU 提供 cmpxchg 指令;而在精簡指令集的體系架構中,則通常是靠一對指令(如“load and reserve”和“store conditional”)實現的。在大多數處理器上 CAS 都是非常輕量級的操作,這也是其優勢所在。
Java 的 CAS 操作
CAS 依賴於 Unsafe 類提供的一些底層能力,進行底層操作。
/**
* Atomically update Java variable to x if it is currently
* holding expected.
* @return true if successful
*/
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
在調用 compareAndSwap() 方法時,我們需要傳入需要修改的共用變數、對象偏移量、我們期望的變數當前值、要寫回的值。如果變數的當前值和我們的期望值相等,則寫回成功,返回 true,否則寫回失敗,返回 false。
Unsafe 類是 Java 提供的一個操作記憶體的非安全類,操作對象和對應的變數來完成 CAS 操作。顯然 Unsafe 類過於底層,調用 Unsafe 類的方法不是大多數應用場景的最好選擇。目前 Java 提供了兩種公共 API,可以實現 CAS 操作:
- 一種是 Atomic 原子類。Atomic 包中的類對 Unsafe 類進行了封裝,使我們可以更方便的使用 CAS 操作。Atomic 包提供了常見的原子性數據類型,甚至是引用、數組等相關原子類型和原子更新操作工具。
- 還有一種是 Variable Handle API,它源自於JEP 193,提供了各種粒度的原子或者有序性的操作等。
CAS 的優劣局限
CAS 的優點:在大多數處理器上 CAS 都是非常輕量級的操作。
CAS 的局限:
- CAS 操作是針對一個共用變數的,如果需要解決多個變數的原子性問題,建議還是使用互斥鎖方案。
- 存在 ABA 問題:當一個線程在進行 CAS 操作時,另一個線程可能會在此期間修改了同一個共用變數的值,然後又將其改回原來的值。這種情況下,CAS 操作就無法檢測到共用變數值的變化,從而導致 ABA 問題。如果我們僅僅在寫回數據前判斷數值是 A,可能導致不合理的寫回操作。針對這種情況,Java 提供了 AtomicStampedReference 工具類,通過為對象引用建立類似版本號(stamp)的方式,來解決 ABA 問題,保證 CAS 的正確性。
- 如果有大量的線程同時對一個共用變數進行 CAS 操作,競爭過於激烈的情況下,嘗試進行 CAS 操作的線程只會白白消耗處理器資源,而不會做任何有價值的工作,這就會帶來性能的浪費。
本文來自博客園,作者:真正的飛魚,轉載請註明原文鏈接:https://www.cnblogs.com/feiyu2/p/CAS.html