問二十一:說說CAS是什麼(概念)?

導入

synchronized這樣的鎖是悲觀鎖,始終假定會發生併發衝突,會屏蔽一切可能違反數據完整性的約束

樂觀鎖假設不會發生併發衝突,所以它只會在提交時檢查是否保證數據完整性,如果提交失敗則會進行重試,而最常見的就是CAS(Compare and Swap)了


CAS

一種高效實現線程安全性的方法

    支持原子更新操作,適用於計數器,序列發生器(就是給變量自增的工具)等場景

    屬於樂觀鎖機制,號稱lock-free(底層也是有加鎖行爲的)

    CAS操作失敗是,由開發者決定是否繼續嘗試,還是其他操作,因此爭用CAS失敗的線程不會被阻塞掛起


CAS思想

包含三個操作數——內存位置(V)預期原值(A)新值(B):當一個線程需要修改一個共享變量的值,完成這個操作需要先取出共享變量的值,賦給A基於A進行計算,得到新值B,在用預期原值A和內存中的共享變量值進行比較(這可以通過一個簡單的布爾響應(這個變體通常稱爲比較和設置),或通過返回從內存位置讀取的值來完成),如果相同就認爲其他線程沒有進行修改,而將新值寫入內存


JAVA中CAS的實現

JAVA1.5開始引入了CAS,主要代碼都放在JUC的atomic包下

分析其中一個,比如AtomicInteger,它使用到了Unsafe這個類


CAS的缺點

CPU開銷比較大:在併發量比較高的情況下,如果許多線程反覆嘗試更新某一個變量,卻又一直更新不成功,又因爲自旋的時候會一直佔用CPU,如果CAS一直更新不成功就會一直佔用,造成CPU的浪費。

 

不能保證代碼塊的原子性:CAS機制所保證的只是一個變量的原子性操作,而不能保證整個代碼塊的原子性。比如需要保證3個變量共同進行原子性的更新,就不得不使用Synchronized了。

 

ABA問題:線程1從內存X中取出A,這時候另一個線程2也從內存X中取出A,並且線程2進行了一些操作將內存X中的值變成了B,然後線程2又將內存X中的數據變成A,這時候線程1進行CAS操作發現內存X中仍然是A,然後線程1操作成功。雖然線程1的CAS操作成功,但是整個過程就是有問題的。


CAS引起的ABA問題

比如說我要取100,但是由於機器故障開啓了兩次取錢的線程,由於採用的是CAS操作,線程一如果執行成功,而線程二由於某些原因阻塞了,並且在這個階段中有其他用戶向我的賬戶中轉了50元,並且該線程執行成功,那麼線程二恢復的時候,會發現地址的值V=100,線程二比較V、A發現相同,就更新餘額爲50.這樣就發生錯誤。


CAS的ABA問題解決方案

不是隻是更新某個引用的值, 而是更新兩個值,包含一個引用和一個版本號

AtomicStampedReference以及AtomicMarkableReference支持在兩個變量上執行原子的條件更新。

AtomicStampedReference將更新一個“對象引用——版本號”二元組,通過在引用上加上“版本號”,從而避免ABA問題(reference——stamp

AtomicMarkableReference將更新一個“對象引用——布爾值”二元組,在某些算法中將通過這種二元組使節點保存在鏈表中同時又將其標記爲“已刪除節點”(reference——mark

 

CAS切換時,內核的變化是什麼樣的呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章