教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

排他鎖和共享鎖分別是什么?有什么不同?

更新時間:2020年10月30日18時14分 來源:傳智播客 瀏覽次數:

分布式鎖是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。如果不同的系統(tǒng)或是同一個系統(tǒng)的不同主機之間共享了一個或一組資源,那么訪問這些資源的時候,往往需要通過一些互斥手段來防止彼此之間的干擾,以保證一致性,在這種情況下,就需要使用分布式鎖了。

在平時的實際項目開發(fā)中,我們往往很少會去在意分布式鎖,而是依賴于關系型數據庫固有的排他性來實現(xiàn)不同進程之間的互斥。這確實是一種非常簡便且被廣泛使用的分布式鎖實現(xiàn)方式。然而有一個不爭的事實是,目前絕大多數大型分布式系統(tǒng)的性能瓶頸都集中在數據庫操作上。因此,如果上層業(yè)務再給數據庫添加一些額外的鎖,例如行鎖、表鎖甚至是繁重的事務處理,那么就會讓數據庫更加不堪重負。

下面我們來看看使用ZooKeeper如何實現(xiàn)分布式鎖,這里主要講解排他鎖共享鎖兩類分布式鎖。

排他鎖

排他鎖(Exclusive Locks,簡稱 X 鎖),又稱為寫鎖或獨占鎖,是一種基本的鎖類型。如果事務 T1對數據對象 O1加上了排他鎖,那么在整個加鎖期間,只允許事務 T1對 O1進行讀取和更新操作,其他任何事務都不能再對這個數據對象進行任何類型的操作——直到T1釋放了排他鎖

從上面講解的排他鎖的基本概念中,我們可以看到,排他鎖的核心是如何保證當前有且僅有一個事務獲得鎖,并且鎖被釋放后,所有正在等待獲取鎖的事務都能夠被通知到。

下面我們就來看看如何借助ZooKeeper實現(xiàn)排他鎖:

① 定義鎖

在通常的Java開發(fā)編程中,有兩種常見的方式可以用來定義鎖,分別是synchronized機制和JDK5提供的ReentrantLock。然而,在ZooKeeper中,沒有類似于這樣的API可以直接使用,而是通過 ZooKeeper 上的數據節(jié)點來表示一個鎖,例如/exclusive_lock/lock節(jié)點就可以被定義為一個鎖,如圖:

1604046911092_分布式鎖01.jpg

② 獲取鎖

在需要獲取排他鎖時,所有的客戶端都會試圖通過調用 create()接口,在/exclusive_lock節(jié)點下創(chuàng)建臨時子節(jié)點/exclusive_lock/lock。在前面,我們也介紹了,ZooKeeper 會保證在所有的客戶端中,最終只有一個客戶端能夠創(chuàng)建成功,那么就可以認為該客戶端獲取了鎖。同時,所有沒有獲取到鎖的客戶端就需要到/exclusive_lock 節(jié)點上注冊一個子節(jié)點變更的Watcher監(jiān)聽,以便實時監(jiān)聽到lock節(jié)點的變更情況

③釋放鎖

在“定義鎖”部分,我們已經提到,/exclusive_lock/lock 是一個臨時節(jié)點,因此在以下兩種情況下,都有可能釋放鎖。 · 當前獲取鎖的客戶端機器發(fā)生宕機,那么ZooKeeper上的這個臨時節(jié)點就會被移除。 · 正常執(zhí)行完業(yè)務邏輯后,客戶端就會主動將自己創(chuàng)建的臨時節(jié)點刪除。 無論在什么情況下移除了lock節(jié)點,ZooKeeper都會通知所有在/exclusive_lock節(jié)點上注冊了子節(jié)點變更Watcher監(jiān)聽的客戶端。這些客戶端在接收到通知后,再次重新發(fā)起分布式鎖獲取,即重復“獲取鎖”過程。整個排他鎖的獲取和釋放流程,如下圖:

1604046922535_分布式鎖02.jpg


共享鎖

共享鎖(Shared Locks,簡稱S鎖),又稱為讀鎖,同樣是一種基本的鎖類型。

如果事務T1對數據對象O1加上了共享鎖,那么當前事務只能對O1進行讀取操作,其他事務也只能對這個數據對象加共享鎖——直到該數據對象上的所有共享鎖都被釋放。

共享鎖和排他鎖最根本的區(qū)別在于,加上排他鎖后,數據對象只對一個事務可見,而加上共享鎖后,數據對所有事務都可見。

下面我們就來看看如何借助ZooKeeper來實現(xiàn)共享鎖。

① 定義鎖
和排他鎖一樣,同樣是通過 ZooKeeper 上的數據節(jié)點來表示一個鎖,是一個類似于“/shared_lock/[Hostname]-請求類型-序號”的臨時順序節(jié)點,例如/shared_lock/host1-R-0000000001,那么,這個節(jié)點就代表了一個共享鎖,如圖所示:

1604046935038_分布式鎖03.jpg

② 獲取鎖

在需要獲取共享鎖時,所有客戶端都會到/shared_lock 這個節(jié)點下面創(chuàng)建一個臨時順序節(jié)點,如果當前是讀請求,那么就創(chuàng)建例如/shared_lock/host1-R-0000000001的節(jié)點;如果是寫請求,那么就創(chuàng)建例如/shared_lock/host2-W-0000000002的節(jié)點。

判斷讀寫順序

通過Zookeeper來確定分布式讀寫順序,大致分為四步

(1)創(chuàng)建完節(jié)點后,獲取/shared_lock節(jié)點下所有子節(jié)點,并對該節(jié)點變更注冊監(jiān)聽。

(2)確定自己的節(jié)點序號在所有子節(jié)點中的順序。

(3)對于讀請求:若沒有比自己序號小的子節(jié)點或所有比自己序號小的子節(jié)點都是讀請求,那么表明自己已經成功獲取到共享鎖,同時開始執(zhí)行讀取邏輯,若有寫請求,則需要等待。對于寫請求:若自己不是序號最小的子節(jié)點,那么需要等待。

(4)接收到Watcher通知后,重復步驟1

③ 釋放鎖,其釋放鎖的流程與獨占鎖一致。


猜你喜歡:

自旋鎖原理是什么?自旋鎖有什么優(yōu)缺點?

Java無鎖并發(fā)編程教程 

0 分享到:
和我們在線交談!