zk系列三:zookeeper實戰之分布式鎖實現( 二 )

提示:代碼中注釋的代碼塊可以關注下,原本是直接阻塞式編程,將獲取所有子節點并釋放鎖的操作直接寫在getChildren方法的回調里,后來發現當節點被刪除時我們還要重新搶鎖,那么代碼就冗余了,于是結合響應式編程的思想,將這段核心代碼放到getChildren方法的回調里 , 這樣代碼簡潔了并且可以讓業務更只關注于getChildren這件事了
2、測試代碼編寫線程安全問題復現package com.darling.service.zookeeper.lock;import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import org.junit.Test;/** * @description:開啟是個線程給i做遞減操作,未加鎖的情況下會有線程安全問題 * @author: dll * @date: Created in 2022/11/8 8:32 * @version: * @modified By: */@Slf4jpublic class ZkLockTest02 {private int i = 10;@Testpublic void test() throws InterruptedException {for (int n = 0; n < 10; n++) {new Thread(new Runnable() {@SneakyThrows@Overridepublic void run() {Thread.sleep(100);incre();}}).start();}Thread.sleep(5000);log.info("i = {}",i);}/*** i遞減 線程不安全*/public void incre(){//i.incrementAndGet();log.info("當前線程:{},i = {}",Thread.currentThread().getName(),i--);}}
  • 上面代碼運行結果如下:
    zk系列三:zookeeper實戰之分布式鎖實現

    文章插圖
使用上面封裝的ZkLockHelper實現的分布式鎖package com.darling.service.zookeeper.lock;import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import org.apache.zookeeper.ZooKeeper;import org.junit.After;import org.junit.Before;import org.junit.Test;/** * @description: 使用zk實現的分布式鎖解決線程安全問題 * @author: dll * @date: Created in 2022/11/8 8:32 * @version: * @modified By: */@Slf4jpublic class ZkLockTest03 {ZooKeeper zkClient;@Beforepublic void conn (){zkClient= ZkUtil.getZkClient();}@Afterpublic void close (){try {zkClient.close();} catch (InterruptedException e) {e.printStackTrace();}}private int i = 10;@Testpublic void test() throws InterruptedException {for (int n = 0; n < 10; n++) {new Thread(new Runnable() {@SneakyThrows@Overridepublic void run() {Thread.sleep(100);ZkLockHelper zkHelper = new ZkLockHelper();// 這里給zkHelper設置threadName是為了后續調試的時候日志打??,遍斣徾昌W嬖詰奈侍?String threadName = Thread.currentThread().getName();zkHelper.setThreadName(threadName);zkHelper.setZkClient(zkClient);// tryLock上鎖zkHelper.tryLock();incre();log.info("線程{}正在執行業務代碼...",threadName);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 釋放鎖zkHelper.unLock();}}).start();}while (true) {}}/*** i遞減 線程不安全*/public void incre(){//i.incrementAndGet();log.info("☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆當前線程:{},i = {}",Thread.currentThread().getName(),i--);}}
  • 運行結果如下:
    zk系列三:zookeeper實戰之分布式鎖實現

    文章插圖
【zk系列三:zookeeper實戰之分布式鎖實現】由于日志中摻雜著zk的日志所有此處并未截全,但是也能看到i是在按規律遞減的,不會出現通過線程拿到相同值的情況
四、zk實現分布式鎖的優缺點優點
  • 集群部署不存在單點故障問題
  • 統一視圖zk集群每個節點對外提供的數據是一致的,數據一致性有所報障
  • 臨時有序節點zk提供臨時有序節點,這樣當客戶端失去連接時會自動釋放鎖,不用像其他方案一樣當拿到鎖的實例服務不可用時,需要定時任務去刪除鎖;臨時節點的特性就是當客戶端失去連接會自動刪除
  • watch能力加持當獲取不到鎖時,無需客戶端定期輪詢爭搶,只需watch前一節點即可 , 當有變化時會及時通知,比普通方案即及時又高效;注意這里最好只watch前一節點,如果watch整個父目錄的話,當客戶端并發較大時會不斷有請求進出zk,給zk性能帶來壓力
缺點
  • 與單機版redis比較的話性能肯定較差,但是當客戶端集群足夠龐大且業務量足夠多時肯定還是集群更加穩定
好了,zk實現分布式鎖的編碼實現就到這了 , 后續有時間再寫偏redis的,其實思路縷清了,編碼實現還是很簡單的

推薦閱讀