一文秒懂Java中的樂觀鎖 VS 悲觀鎖

樂觀鎖 VS 悲觀鎖

悲觀鎖:總是假設最壞的情況,每次取數據時都認為其他線程會修改,所以都會加鎖(讀鎖、寫鎖、行鎖等),當其他線程想要訪問數據時,都需要阻塞掛起。

樂觀鎖:總是認為不會產生並發問題,每次去取數據的時候總認為不會有其他線程對數據進行修改,因此不會上鎖,但是在更新時會判斷其他線程在這之前有沒有對數據進行修改。
樂觀鎖在Java中通過使用無鎖來實現,常用的是CAS,Java中原子類的遞增就是通過CAS自旋實現。

在這裡插入圖片描述

CAS

CAS全稱 Compare And Swap(比較與交換),是一種無鎖算法。在不使用鎖(沒有線程被阻塞)的情況下實現多線程之間的變量同步。java.util.concurrent包中的原子類就是通過CAS來實現瞭樂觀鎖。

一個 CAS 涉及到以下操作:
我們假設內存中的原數據V,舊的預期值A,需要修改的新值B,

  1. 比較 A 與 V 是否相等。(比較)
  2. 如果比較相等,將 B 寫入 V。(交換)
  3. 返回操作是否成功。

CAS的底層原理

  • 調用 Unsafe 類中的 CAS 方法,JVM 會幫我們實現出 CAS 匯編指令
  • 這是一種完全依賴於硬件的功能,通過它實現原子操作
  • 原語的執行必須是連續的,在執行過程中不允許被中斷,CAS 是 CUP 的一條原子指令

CAS的三大問題

  • 如果 CAS 長時間一直不成功,會給 CPU 帶來很大的開銷,在Java的實現中是一隻通過while循環自旋CAS獲取鎖。
  • 隻能保證一個共享變量的原子操作
  • 引出瞭 ABA 問題

ABA問題

CAS需要在操作值的時候檢查內存值是否發生變化,沒有發生變化才會更新內存值。但是如果內存值原來是A,後來變成瞭B,然後又變成瞭A,那麼CAS進行檢查時會發現值沒有發生變化,但是實際上是有變化的。

如何解決ABA問題
加入版本信息,例如攜帶 AtomicStampedReference 之類的時間戳作為版本信息,保證不會出現老的值。

UnSafe

Unsafe類是在sun.misc包下,不屬於Java標準。但是很多Java的基礎類庫,包括一些被廣泛使用的高性能開發庫都是基於Unsafe類開發的,比如Netty、Cassandra、Hadoop、Kafka等。Unsafe類在提升Java運行效率,增強Java語言底層操作能力方面起瞭很大的作用。

使用Unsafe可用來直接訪問系統內存資源並進行自主管理,Unsafe類在提升Java運行效率,增強Java語言底層操作能力方面起瞭很大的作用。

Unsafe可認為是Java中留下的後門,提供瞭一些低層次操作,如直接內存訪問、線程調度等。

這個類的提供瞭一些繞開JVM的更底層功能,基於它的實現可以提高效率。但是,它是一把雙刃劍:正如它的名字所預示的那樣,它是Unsafe的,它所分配的內存需要手動free(不被GC回收)。如果對Unsafe類理解的不夠透徹,就進行使用的話,就等於給自己挖瞭無形之坑,最為致命。

到此這篇關於一文秒懂樂觀鎖 VS 悲觀鎖的文章就介紹到這瞭,更多相關java樂觀鎖 悲觀鎖內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: