ZooKeeper 分布式鎖 Curator 源碼之三:可重入鎖并發(fā)加鎖
前言
在了解了加鎖和鎖重入之后,最需要了解的還是在分布式場景下或者多線程并發(fā)加鎖是如何處理的?
1并發(fā)加鎖
先來看結(jié)果,在多線程對 /locks/lock_01 加鎖時,是在后面又創(chuàng)建了新的臨時節(jié)點。
這塊在加鎖方法 CreateBuilderImpl#pathInForeground 中已經(jīng)介紹過
這里判斷 /locks/lock_01 路徑已經(jīng)存在,會直接創(chuàng)建新的臨時順序節(jié)點。
真正判斷鎖是否獲取成功,其實是在 LockInternals#attemptLock 方法中的 internalLockLoop 方法中。
加鎖結(jié)果及監(jiān)聽
internalLockLoop 方法的主要作用是判斷加鎖結(jié)果,以及獲取鎖失敗時,對其他節(jié)點的監(jiān)聽。
- 獲取父節(jié)點 /locks/lock_01 下的所有子節(jié)點,按照從小到大排序,判斷自己是不是獲取到鎖,沒有獲取到就監(jiān)聽自己前一個節(jié)點;
- 支持設(shè)置超時時間,超時直接返回失敗;
- 不支持設(shè)置超時時間或者還沒有超時,則直接 wait 等待。
是否獲取鎖的代碼在 StandardLockInternalsDriver#getsTheLock
這塊就是判斷是否為最小節(jié)點,因為在 getSortedChildren 中已經(jīng)對所有節(jié)點排序,所以方法中的 List
maxLeases 是在 InterProcessMutex 初始化的時候,指定的值為 1。
最終這里的結(jié)果是,判斷自己是不是最小,不是最小,就將 pathToWatch 設(shè)置為前一個節(jié)點。
只監(jiān)聽自己的前一個節(jié)點,可以避免羊群效應(yīng)!
為什么要進行等待呢?
因為是為了防止無效自旋,因為這里有監(jiān)聽機制,會監(jiān)聽上一個節(jié)點是否釋放。
這塊是 ZooKeeper 的 Watcher 監(jiān)聽機制,在節(jié)點釋放的時候,會進行回調(diào),然后使用 Java 的 notifyAll 方法通知所有的 wait 線程。然后這里的 while trye 會繼續(xù)執(zhí)行,重新檢查是否獲得鎖等。
2總結(jié)
本文主要介紹了基于 ZooKeeper 的分布式鎖框架 Curator 在并發(fā)場景下的鎖競爭問題。
重點需要了解的是:
- 為了避免羊群效應(yīng),臨時順序節(jié)點,加鎖失敗后監(jiān)聽的是前一個節(jié)點;
- 為了避免無效自旋,這里使用了 Java 的 wait/notifyAll 機制;
- 可以看出,默認加鎖就是公平鎖。
本文轉(zhuǎn)載自微信公眾號「程序員小航」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系程序員小航公眾號。