樂觀鎖
定義
在操作時持樂觀態度,認爲操作時其它線程不會修改數據,因此不會鎖定數據,但是在更新數據時會用版本號或者CAS算法判斷數據在本次操作過程中是否被更改,如果被更改,則修改失敗。
所以樂觀鎖雖然名字帶鎖,但是實際上並不會對數據進行鎖定操作,其它線程仍然可以自由地讀寫數據,不會造成死鎖等問題。
適用場景
樂觀鎖不會鎖定數據,所以其它需要讀取數據的線程不需要等待,但是如果更新頻繁,頻繁地出現修改失敗回滾的情況,反而會導致性能及使用體驗的問題。
因此樂觀鎖適用於需要頻繁讀取數據,但是較少更新數據的場景。
實際應用
Java中原子變量使用了CAS算法來避免加鎖,同時保證數據修改的正確性:
-
使用volatile聲明該變量爲線程間共享變量;
-
使用CAS算法保證變量修改的正確性,我們可以調用compareAndSet函數來嘗試修改原子變量,該函數會返回修改的結果,如果成功,則操作完成,如果失敗則獲取新值處理後繼續嘗試;
實現方式
樂觀鎖通常有兩種實現方式:
通過版本號機制實現;
通過CAS(compare and swap)算法實現;
版本號機制實現
- 取出數據時同時獲取數據當前的version;
- 更新數據時也帶上這個version;
- 服務端比對請求的version與目前數據的version是否一致;
- 如果一致,則更新數據,如果不一致,則修改失敗;
CAS算法實現
CAS算法簡介
CAS是compare and swap的縮寫,算法邏輯就是當一個線程要修改某個數據時,需要攜帶上之前的原數據,服務端會將這個原數據與當前數據進行比對,如果一致,則將當前數據替換爲申請修改的數據,如果不一致,則修改失敗。
ABA問題
- 線程T1與T2均取出A數據;
- T1要將A數據更新爲B,而此時T2速度更快,將數據從A更新爲了C,然後又將C更新爲了A;
- T1的更新請求到達服務端,服務端比對T1攜帶的原數據A與當前數據A,結果相等,於是將數據更新爲了B,但實際數據已經幾經變化了;
在大多數情況下,這都是沒有問題的,但如果我們需要嚴格保證原數據未經變動時,則可能出現問題,相比較使用版本號實現的樂觀鎖則可以避免此問題。
悲觀鎖
定義
在操作時持悲觀態度,認爲其它線程會修改數據,所以更新數據時對數據進行鎖定,加鎖後,同一時間只能有一個線程執行,其它線程只能等待直到鎖被其它線程釋放。
適用場景
悲觀鎖可以保證數據操作時的正確性,不會出現數據因被其它線程修改而失敗的情況,但是它會限制其它線程讀取數據。
所以悲觀鎖更適用於修改頻繁,使用樂觀鎖會頻繁衝突回滾,但讀取較少的場景。
實際應用
Java的synchronized關鍵字就是一種悲觀鎖