ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖

程式設計師小航 發表於 2021-07-22
ZooKeeper

前言

在瞭解了加鎖和鎖重入之後,最需要了解的還是在分散式場景下或者多執行緒併發加鎖是如何處理的?

併發加鎖

ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖

先來看結果,在多執行緒對 /locks/lock_01 加鎖時,是在後面又建立了新的臨時節點。

這塊在加鎖方法 CreateBuilderImpl#pathInForeground 中已經介紹過

ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖

這裡判斷 /locks/lock_01 路徑已經存在,會直接建立新的臨時順序節點。

真正判斷鎖是否獲取成功,其實是在 LockInternals#attemptLock 方法中的 internalLockLoop 方法中。

ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖

加鎖結果及監聽

internalLockLoop 方法的主要作用是判斷加鎖結果,以及獲取鎖失敗時,對其他節點的監聽。

ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖

  1. 獲取父節點 /locks/lock_01 下的所有子節點,按照從小到大排序,判斷自己是不是獲取到鎖,沒有獲取到就監聽自己前一個節點;
  2. 支援設定超時時間,超時直接返回失敗;
  3. 不支援設定超時時間或者還沒有超時,則直接 wait 等待。

是否獲取鎖的程式碼在 StandardLockInternalsDriver#getsTheLock

ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖

這塊就是判斷是否為最小節點,因為在 getSortedChildren 中已經對所有節點排序,所以方法中的 List<String> children 是有序的。

maxLeases 是在 InterProcessMutex 初始化的時候,指定的值為 1。

最終這裡的結果是,判斷自己是不是最小,不是最小,就將 pathToWatch 設定為前一個節點

只監聽自己的前一個節點,可以避免羊群效應!

為什麼要進行等待呢?

因為是為了防止無效自旋,因為這裡有監聽機制,會監聽上一個節點是否釋放。

ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖

ZooKeeper 分散式鎖 Curator 原始碼 03:可重入鎖併發加鎖

這塊是 ZooKeeper 的 Watcher 監聽機制,在節點釋放的時候,會進行回撥,然後使用 Java 的 notifyAll 方法通知所有的 wait 執行緒。然後這裡的 while trye 會繼續執行,重新檢查是否獲得鎖等。

總結

本文主要介紹了基於 ZooKeeper 的分散式鎖框架 Curator 在併發場景下的鎖競爭問題。

重點需要了解的是:

  1. 為了避免羊群效應,臨時順序節點,加鎖失敗後監聽的是前一個節點
  2. 為了避免無效自旋,這裡使用了 Java 的 wait/notifyAll 機制;
  3. 可以看出,預設加鎖就是公平鎖

相關推薦