數據庫之樂觀鎖與悲觀鎖

(如需要牛客網的java面經電子版本,可以加個關注,私信我,免費分享)
悲觀鎖 :
總是假設最壞的情況,每次去拿數據的時候都認爲別人會修改,所以每次在拿 數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資 源每次只給一個線程使用,其它線程阻塞,用完後再把資源轉讓給其它線 程)。傳統的關係型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖 等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中 synchronized和 ReentrantLock等獨佔鎖就是悲觀鎖思想的實現。

樂觀鎖 :
總是假設最好的情況,每次去拿數據的時候都認爲別人不會修改,所以不會上 鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以 使用版本號機制和CAS算法實現。樂觀鎖適用於多讀的應用類型,這樣可以提 高吞吐量

兩種鎖的使用場景 :
樂觀鎖適用於寫比較少的情況下(多讀場景),即衝突真的很少發生的 時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。
如果是多寫的 情況,一般會經常產生衝突,這就會導致上層應用會不斷的進行retry,這樣反 倒是降低了性能,所以一般多寫的場景下用悲觀鎖就比較合適

樂觀鎖常見的兩種實現方式 :
樂觀鎖一般會使用版本號機制或CAS算法實現。
==1.版本號機制 ==
一般是在數據表中加上一個數據版本號version字段,表示數據被修改的次 數,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數 據的同時也會讀取version值,在提交更新時,若剛纔讀取到的version值爲當 前數據庫中的version值相等時才更新,否則重試更新操作,直到更新成功。
==2. CAS算法 ==
即compare and swap(比較與交換),是一種有名的無鎖算法。無鎖編程, 即不使用鎖的情況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的 情況下實現變量的同步
CAS算法涉及到三個操作數 :

  • 需要讀寫的內存值 V
  • 進行比較的值 A
  • 擬寫入的新值 B
    當且僅當 V 的值等於 A時,CAS通過原子方式用新值B來更新V的值,否則 不會執行任何操作(比較和替換是一個原子操作)。一般情況下是一個自旋操 作,即不斷的重試。

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

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

==3 只能保證一個共享變量的原子操作 ==
CAS 只對單個共享變量有效,當操作涉及跨多個共享變量時 CAS 無效

CAS與synchronized的使用情景:
簡單的來說CAS適用於寫比較少的情況下(多讀場景,衝突一般較少),synchronized適用於寫比較多的情況下(多寫場景,衝突一般較多)

  • 對於資源競爭較少(線程衝突較輕)的情況,使用synchronized同步鎖 進行線程阻塞和喚醒切換以及用戶態內核態間的切換操作額外浪費消耗 cpu資源;而CAS基於硬件實現,不需要進入內核,不需要切換線程, 操作自旋機率較少,因此可以獲得更高的性能

  • 對於資源競爭嚴重(線程衝突嚴重)的情況,CAS自旋的概率會比較 大,從而浪費更多的CPU資源,效率低於synchronized

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