數據庫系列課程(19)-樂觀鎖與悲觀鎖的優缺點

悲觀鎖

總是假設最壞的情況,每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完後再把資源轉讓給其它線程)。Java中synchronized和ReentrantLock等獨佔鎖就是悲觀鎖思想的實現。

樂觀鎖

總是假設最好的情況,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。

兩種鎖的使用場景

從上面對兩種鎖的介紹,我們知道兩種鎖各有優缺點,不可認爲一種好於另一種,像樂觀鎖適用於寫比較少的情況下(多讀場景),即衝突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果是多寫的情況,一般會經常產生衝突,這就會導致上層應用會不斷的進行retry,這樣反倒是降低了性能,所以一般多寫的場景下用悲觀鎖就比較合適。

樂觀鎖常見的兩種實現方式

  • 版本號機制(修改前,先查詢出版本號,然後加where判斷插入)
  • CAS算法實現

樂觀鎖的缺點

缺點一:ABA問題

如果一個變量V初次讀取的時候是A值,並且在準備賦值的時候檢查到它仍然是A值,那我們就能說明它的值沒有被其他線程修改過了嗎?很明顯是不能的,因爲在這段時間它的值可能被改爲其他值,然後又改回A,那CAS操作就會誤認爲它從來沒有被修改過。這個問題被稱爲CAS操作的 "ABA"問題。


JDK 1.5 以後的 AtomicStampedReference 類就提供了此種能力,其中的 compareAndSet方法就是首先檢查當前引用是否等於預期引用,並且當前標誌是否等於預期標誌,如果全部相等,則以原子方式將該引用和該標誌的值設置爲給定的更新值。

缺點二:循環時間長開銷大

  • 自旋CAS(也就是不成功就一直循環執行直到成功)如果長時間不成功,會給CPU帶來非常大的執行開銷。

缺點三:只能保證一個共享變量的原子操作

  • CAS 只對單個共享變量有效,當操作涉及跨多個共享變量時 CAS 無效。但是從 JDK 1.5開始,提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象裏來進行 CAS 操作.所以我們可以使用鎖或者利用AtomicReference類把多個共享變量合併成一個共享變量來操作。

本文部分內容摘自:https://juejin.im/post/5b4977ae5188251b146b2fc8

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