<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從 Java 鎖到分布式鎖

          共 2377字,需瀏覽 5分鐘

           ·

          2022-01-04 21:47

          3ffa90a74592f9388feb8bed3e737873.webp

          前言

          在并發(fā)編程中常用到 synchronized 以及 ReentrantLock 鎖,在業(yè)務(wù)開發(fā)過程中也可能會用到分布式鎖,分布式鎖常用框架的就是基于 Redis 實現(xiàn)的分布式鎖框架 Redisson 和 基于 Zookeeper 實現(xiàn)的分布式鎖框架 Curator。當然,也有其他的鎖實現(xiàn)方式,在這里不做介紹。

          本文主要是在學(xué)習(xí) Java 鎖以及分布式鎖的源碼后,做出的歸納總結(jié)。

          1鎖的最基本要素

          為什么要使用鎖?

          關(guān)于為什么要使用鎖這個問題,答案顯而易見:“為了避免多線程并發(fā)沖突”。

          在多線程中對公共數(shù)據(jù)的修改,必須要保證只有線程在進行操作。這里的公共數(shù)據(jù)可以是公共變量,也可以是數(shù)據(jù)庫中的一行數(shù)據(jù)。

          鎖的基本要素

          知道為什么要加鎖之后,就可以得出加鎖的基本要素:

          1. 鎖標志:怎么樣才算加鎖成功?
          2. 鎖持有者:是哪個線程加的鎖?
          3. 鎖重入:自己加了鎖之后,再次加鎖?
          4. 鎖并發(fā):并發(fā)加鎖失敗的線程該怎么辦?
          5. 鎖釋放:用完鎖,該怎么釋放?

          簡單來說應(yīng)該就是這些要素,遺漏之處,歡迎補充。

          2加鎖標志

          加鎖標志,就是需要一個標志來表示是否加鎖成功,并且這個加鎖標志要保證原子性。

          • synchronized 底層是 C++ 實現(xiàn)的,在 ObjectMonitor 對象中有一個 _count 參數(shù)用來標識是否持有鎖。
          • ReentrantLock 基于 AQS 實現(xiàn),其中 state 表示線程是否持有鎖。ReentrantReadWriteLock 同樣基于 AQS 實現(xiàn),其中 state 高 16 位表示讀鎖,低 16 位表示寫鎖。
          • Redisson 是基于 Redis 的 Hash 數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的,其中加鎖 key 是否存在,可以表示是否加鎖。
          • Curator 基于 ZooKeeper 臨時順序節(jié)點實現(xiàn),判斷加鎖路徑下是否存在該節(jié)點,來表示是否加鎖。

          3鎖持有者

          鎖持有者,肯定是當前線程,但是在分布式鎖中還需要加上機器,用來防止服務(wù)之間的線程沖突。

          • synchronized 在 ObjectMonitor 對象中 _owner 是指獲得鎖的線程。
          • ReentrantLock 和 ReentrantReadWriteLock 是在 AQS 的 Node 節(jié)點中有 Thread 對象,用來表示獲得鎖的線程。
          • Redisson 在 Hash 數(shù)據(jù)結(jié)構(gòu)的 field 字段存放的是 UUID:ThreadId,從而保證在多個服務(wù)實例時防止并發(fā)沖突。
          • Curator 創(chuàng)建的臨時順序節(jié)點包含 UUID,表示加鎖機器,結(jié)構(gòu)比如 /locks/lock_01/_c_UUID-lock-0000000000

          4鎖重入

          當獲得鎖的線程再次嘗試獲取鎖的時候,保證需要計數(shù)。

          • synchronized 會對 _count 進行累加,CAS 更新。
          • ReentrantLock 會對 AQS 的 state 進行累加,CAS 更新。
          • Redisson 使用 Lua 腳本,對 Hash 結(jié)構(gòu)的 value 進行累加。
          • Curator 是在 Java 代碼中維護了一個 AtomicInteger 類型的 lockCount 字段,用來表示重入。

          5鎖等待

          • synchronized 并發(fā)加鎖失敗線程會自旋等待,涉及到偏向鎖、輕量級鎖、重量級鎖的鎖升級流程。
          1. 剛開始是無鎖的
          2. 偏向鎖:一段同步代碼一直被一個線程訪問,這個線程自動獲取鎖,大多數(shù)都是由同一個線程獲取鎖,這就會出現(xiàn)偏向鎖。目的是只有一個線程執(zhí)行同步代碼塊時提高性能,JDK 6 后在 JVM 中默認開啟。對象頭標志位(01-無鎖) 是否偏向鎖標志(1-是偏向鎖)
          3. 輕量級鎖:鎖是偏向鎖時,被其他線程訪問,偏向鎖就升級為輕量級鎖,其他線程通過自旋形式嘗試獲取鎖 對象頭標志位 00
          4. 重量級鎖:只有一個線程等待,該線程是在自旋等待獲取鎖。當自旋一定次數(shù)或者一個持有鎖一個自旋時來了第三個線程,就會升級為重量級鎖。對象頭標志位 10
          • ReentrantLock 會放到 AQS 雙向同步隊列中,監(jiān)聽上一個節(jié)點是否為虛擬頭結(jié)點,是則嘗試獲取鎖。
            • 非公平鎖新線程會默認參加搶鎖,公平鎖會先查看隊列是否為空。
            • 自旋等待時會使用 LockSupport.park() 方法,這時候會讓出 CPU 資源,其他線程會調(diào)用 LockSupport.unpark()。
            • 可以使用 tryLock 方法設(shè)置時間,在指定時間內(nèi)獲取鎖失敗或者被中斷,則會返回加鎖失敗。
          • Redisson 并發(fā)加鎖,失敗線程會獲取到當前鎖的超時時間,然后通過 Semaphore tryAcquire 方法阻塞一定時間后,再次嘗試獲取鎖。
            • 公平鎖會額外使用 Redis 的 List 數(shù)據(jù)結(jié)構(gòu)來當做線程等待隊列,使用 sorted set 有序集合數(shù)據(jù)結(jié)構(gòu)來存放等待線程的順序(score 是超時時間戳)。
          • Curator 并發(fā)加鎖會直接創(chuàng)建臨時順序節(jié)點,然后監(jiān)聽順序節(jié)點中自己的上一個節(jié)點(防止羊群效應(yīng)),默認是公平鎖。

          6鎖釋放

          • synchronized 不需要手動釋放。
          • ReentrantLock 對 state 遞減為 0 后,喚醒后續(xù)節(jié)點,有后續(xù)節(jié)點需要調(diào)用 LockSupport.unpark(s.thread)。
          • Redisson 主動釋放直接刪除 key 即可。超時釋放即服務(wù)宕機,沒有看門狗續(xù)租了,指定時間后,Redis Key 就會被自動釋放。
          • Curator 主動釋放會刪除節(jié)點,如果服務(wù)宕機了,節(jié)點會被自動刪除。

          7總結(jié)

          本文從多個角度總結(jié)分析了鎖和分布式鎖的基本要素,同樣基于 MySQL 等數(shù)據(jù)庫的鎖可以參考實現(xiàn)。

          eb6a755c08f9cea49f44ca1f1d4e1206.webp


          - -




          歷史文章 |?相關(guān)推薦


          1c72bf19f300916a4246ee75087131c0.webp


          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  AAA片网站 | 在线免费看无霉三级黄片 | 免费看日本mv大片网站 | 大香蕉导航 | 黑人狂躁女人高潮视频 |