最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

分析ZooKeeper分布式鎖的實(shí)現(xiàn)

 更新時(shí)間:2021年06月30日 16:57:21   作者:IT王小二  
在分布式的情況下,sychornized 和 Lock 已經(jīng)不能滿足我們的要求了,那么就需要使用第三方的鎖了,這里我們就使用 ZooKeeper 來(lái)實(shí)現(xiàn)一個(gè)分布式鎖

一、分布式鎖方案比較

方案 實(shí)現(xiàn)思路 優(yōu)點(diǎn) 缺點(diǎn)
利用 MySQL 的實(shí)現(xiàn)方案 利用數(shù)據(jù)庫(kù)自身提供的鎖機(jī)制實(shí)現(xiàn),要求數(shù)據(jù)庫(kù)支持行級(jí)鎖 實(shí)現(xiàn)簡(jiǎn)單 性能差,無(wú)法適應(yīng)高并發(fā)場(chǎng)景;容易出現(xiàn)死鎖的情況;無(wú)法優(yōu)雅的實(shí)現(xiàn)阻塞式鎖
利用 Redis 的實(shí)現(xiàn)方案 使用 Setnx 和 lua 腳本機(jī)制實(shí)現(xiàn),保證對(duì)緩存操作序列的原子性 性能好 實(shí)現(xiàn)相對(duì)復(fù)雜,有可能出現(xiàn)死鎖;無(wú)法優(yōu)雅的實(shí)現(xiàn)阻塞式鎖
利用 ZooKeeper 的實(shí)現(xiàn)方案 基于 ZooKeeper 節(jié)點(diǎn)特性及 watch 機(jī)制實(shí)現(xiàn) 性能好,穩(wěn)定可靠性高,能較好地實(shí)現(xiàn)阻塞式鎖 實(shí)現(xiàn)相對(duì)復(fù)雜

二、ZooKeeper實(shí)現(xiàn)分布式鎖

這里使用 ZooKeeper 來(lái)實(shí)現(xiàn)分布式鎖,以50個(gè)并發(fā)請(qǐng)求來(lái)獲取訂單編號(hào)為例,描述兩種方案,第一種為基礎(chǔ)實(shí)現(xiàn),第二種在第一種基礎(chǔ)上進(jìn)行了優(yōu)化。

2.1、方案一

流程描述:

具體代碼:

OrderNumGenerator:

/**
 * @Description 生成隨機(jī)訂單號(hào)
 */
public class OrderNumGenerator {

    private static long count = 0;

    /**
     * 使用日期加數(shù)值拼接成訂單號(hào)
     */
    public String getOrderNumber() throws Exception {
        String date = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
        String number = new DecimalFormat("000000").format(count++);
        return date + number;
    }
}

Lock:

/**
 * @Description 自定義鎖接口
 */
public interface Lock {

    /**
     * 獲取鎖
     */
    public void getLock();

    /**
     * 釋放鎖
     */
    public void unLock();
}

AbstractLock:

/**
 * @Description 定義一個(gè)模板,具體的方法由子類(lèi)來(lái)實(shí)現(xiàn)
 */
public abstract class AbstractLock implements Lock {

    /**
     * 獲取鎖
     */
    @Override
    public void getLock() {

        if (tryLock()) {
            System.out.println("--------獲取到了自定義Lock鎖的資源--------");
        } else {
            // 沒(méi)拿到鎖則阻塞,等待拿鎖
            waitLock();
            getLock();
        }

    }

    /**
     * 嘗試獲取鎖,如果拿到了鎖返回true,沒(méi)有拿到則返回false
     */
    public abstract boolean tryLock();

    /**
     * 阻塞,等待獲取鎖
     */
    public abstract void waitLock();
}

ZooKeeperAbstractLock:

/**
 * @Description 定義需要的服務(wù)連接
 */
public abstract class ZooKeeperAbstractLock extends AbstractLock {

    private static final String SERVER_ADDR = "192.168.182.130:2181,192.168.182.131:2181,192.168.182.132:2181";

    protected ZkClient zkClient = new ZkClient(SERVER_ADDR);

    protected static final String PATH = "/lock";
}

ZooKeeperDistrbuteLock:

/**
 * @Description 真正實(shí)現(xiàn)鎖的細(xì)節(jié)
 */
public class ZooKeeperDistrbuteLock extends ZooKeeperAbstractLock {
    private CountDownLatch countDownLatch = null;

    /**
     * 嘗試拿鎖
     */
    @Override
    public boolean tryLock() {
        try {
            // 創(chuàng)建臨時(shí)節(jié)點(diǎn)
            zkClient.createEphemeral(PATH);
            return true;
        } catch (Exception e) {
            // 創(chuàng)建失敗報(bào)異常
            return false;
        }
    }

    /**
     * 阻塞,等待獲取鎖
     */
    @Override
    public void waitLock() {
        // 創(chuàng)建監(jiān)聽(tīng)
        IZkDataListener iZkDataListener = new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {

            }

            @Override
            public void handleDataDeleted(String s) throws Exception {
                // 釋放鎖,刪除節(jié)點(diǎn)時(shí)喚醒等待的線程
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };

        // 注冊(cè)監(jiān)聽(tīng)
        zkClient.subscribeDataChanges(PATH, iZkDataListener);

        // 節(jié)點(diǎn)存在時(shí),等待節(jié)點(diǎn)刪除喚醒
        if (zkClient.exists(PATH)) {
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 刪除監(jiān)聽(tīng)
        zkClient.unsubscribeDataChanges(PATH, iZkDataListener);
    }

    /**
     * 釋放鎖
     */
    @Override
    public void unLock() {
        if (zkClient != null) {
            System.out.println("釋放鎖資源");
            zkClient.delete(PATH);
            zkClient.close();
        }
    }
}

測(cè)試效果:使用50個(gè)線程來(lái)并發(fā)測(cè)試ZooKeeper實(shí)現(xiàn)的分布式鎖

/**
 * @Description 使用50個(gè)線程來(lái)并發(fā)測(cè)試ZooKeeper實(shí)現(xiàn)的分布式鎖
 */
public class OrderService {

    private static class OrderNumGeneratorService implements Runnable {

        private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();;
        private Lock lock = new ZooKeeperDistrbuteLock();

        @Override
        public void run() {
            lock.getLock();
            try {
                System.out.println(Thread.currentThread().getName() + ", 生成訂單編號(hào):"  + orderNumGenerator.getOrderNumber());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unLock();
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("----------生成唯一訂單號(hào)----------");
        for (int i = 0; i < 50; i++) {
            new Thread(new OrderNumGeneratorService()).start();
        }
    }
}

2.2、方案二

方案二在方案一的基礎(chǔ)上進(jìn)行優(yōu)化,避免產(chǎn)生“羊群效應(yīng)”,方案一一旦臨時(shí)節(jié)點(diǎn)刪除,釋放鎖,那么其他在監(jiān)聽(tīng)這個(gè)節(jié)點(diǎn)變化的線程,就會(huì)去競(jìng)爭(zhēng)鎖,同時(shí)訪問(wèn) ZooKeeper,那么怎么更好的避免各線程的競(jìng)爭(zhēng)現(xiàn)象呢,就是使用臨時(shí)順序節(jié)點(diǎn),臨時(shí)順序節(jié)點(diǎn)排序,每個(gè)臨時(shí)順序節(jié)點(diǎn)只監(jiān)聽(tīng)它本身的前一個(gè)節(jié)點(diǎn)變化。

流程描述:

具體代碼

具體只需要將方案一中的 ZooKeeperDistrbuteLock 改變,增加一個(gè) ZooKeeperDistrbuteLock2,測(cè)試代碼中使用 ZooKeeperDistrbuteLock2 即可測(cè)試,其他代碼都不需要改變。

/**
 * @Description 真正實(shí)現(xiàn)鎖的細(xì)節(jié)
 */
public class ZooKeeperDistrbuteLock2 extends ZooKeeperAbstractLock {

    private CountDownLatch countDownLatch = null;
    /**
     * 當(dāng)前請(qǐng)求節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)
     */
    private String beforePath;
    /**
     * 當(dāng)前請(qǐng)求的節(jié)點(diǎn)
     */
    private String currentPath;

    public ZooKeeperDistrbuteLock2() {
        if (!zkClient.exists(PATH)) {
            // 創(chuàng)建持久節(jié)點(diǎn),保存臨時(shí)順序節(jié)點(diǎn)
            zkClient.createPersistent(PATH);
        }
    }

    @Override
    public boolean tryLock() {
        // 如果currentPath為空則為第一次嘗試拿鎖,第一次拿鎖賦值currentPath
        if (currentPath == null || currentPath.length() == 0) {
            // 在指定的持久節(jié)點(diǎn)下創(chuàng)建臨時(shí)順序節(jié)點(diǎn)
            currentPath = zkClient.createEphemeralSequential(PATH + "/", "lock");
        }
        // 獲取所有臨時(shí)節(jié)點(diǎn)并排序,例如:000044
        List<String> childrenList = zkClient.getChildren(PATH);
        Collections.sort(childrenList);

        if (currentPath.equals(PATH + "/" + childrenList.get(0))) {
            // 如果當(dāng)前節(jié)點(diǎn)在所有節(jié)點(diǎn)中排名第一則獲取鎖成功
            return true;
        } else {
            int wz = Collections.binarySearch(childrenList, currentPath.substring(6));
            beforePath = PATH + "/" + childrenList.get(wz - 1);
        }
        return false;
    }

    @Override
    public void waitLock() {
        // 創(chuàng)建監(jiān)聽(tīng)
        IZkDataListener iZkDataListener = new IZkDataListener() {
            @Override
            public void handleDataChange(String s, Object o) throws Exception {

            }

            @Override
            public void handleDataDeleted(String s) throws Exception {
                // 釋放鎖,刪除節(jié)點(diǎn)時(shí)喚醒等待的線程
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };

        // 注冊(cè)監(jiān)聽(tīng),這里是給排在當(dāng)前節(jié)點(diǎn)前面的節(jié)點(diǎn)增加(刪除數(shù)據(jù)的)監(jiān)聽(tīng),本質(zhì)是啟動(dòng)另外一個(gè)線程去監(jiān)聽(tīng)前置節(jié)點(diǎn)
        zkClient.subscribeDataChanges(beforePath, iZkDataListener);

        // 前置節(jié)點(diǎn)存在時(shí),等待前置節(jié)點(diǎn)刪除喚醒
        if (zkClient.exists(beforePath)) {
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 刪除對(duì)前置節(jié)點(diǎn)的監(jiān)聽(tīng)
        zkClient.unsubscribeDataChanges(beforePath, iZkDataListener);
    }

    /**
     * 釋放鎖
     */
    @Override
    public void unLock() {
        if (zkClient != null) {
            System.out.println("釋放鎖資源");
            zkClient.delete(currentPath);
            zkClient.close();
        }
    }
}

以上就是分析ZooKeeper分布式鎖的實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于ZooKeeper分布式鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用Java實(shí)現(xiàn)文件流轉(zhuǎn)base64

    使用Java實(shí)現(xiàn)文件流轉(zhuǎn)base64

    這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)文件流轉(zhuǎn)base64效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • JAVA--HashMap熱門(mén)面試題

    JAVA--HashMap熱門(mén)面試題

    這篇文章主要介紹了JAVA關(guān)于HashMap容易被提問(wèn)的面試題,文中題目提問(wèn)頻率高,相信對(duì)你的面試有一定幫助,想要入職JAVA的朋友可以了解下
    2020-06-06
  • Java使用延時(shí)隊(duì)列搞定超時(shí)訂單處理的場(chǎng)景

    Java使用延時(shí)隊(duì)列搞定超時(shí)訂單處理的場(chǎng)景

    這篇文章主要介紹了Java使用延時(shí)隊(duì)列搞定超時(shí)訂單處理,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • SpringBoot實(shí)戰(zhàn)項(xiàng)目之谷歌瀏覽器全屏效果實(shí)現(xiàn)

    SpringBoot實(shí)戰(zhàn)項(xiàng)目之谷歌瀏覽器全屏效果實(shí)現(xiàn)

    這篇文章主要介紹了通過(guò) Java SpringBoot來(lái)實(shí)現(xiàn)谷歌瀏覽器的全屏效果,希望頁(yè)面展示時(shí)可以實(shí)現(xiàn)全屏效果以提高用戶體驗(yàn)。感興趣的小伙伴跟著小編往下看吧
    2021-09-09
  • IDEA2022創(chuàng)建Maven Web項(xiàng)目教程(圖文)

    IDEA2022創(chuàng)建Maven Web項(xiàng)目教程(圖文)

    本文主要介紹了IDEA2022創(chuàng)建Maven Web項(xiàng)目教程,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Dom4j解析XML_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Dom4j解析XML_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了Dom4j解析XML,dom4j是一個(gè)Java的XML API,類(lèi)似于jdom,用來(lái)讀寫(xiě)XML文件的,有興趣的可以了解一下
    2017-07-07
  • java中BeanNotOfRequiredTypeException的問(wèn)題解決(@Autowired和@Resource注解的不同)

    java中BeanNotOfRequiredTypeException的問(wèn)題解決(@Autowired和@Resourc

    本文主要介紹了java中BeanNotOfRequiredTypeException的問(wèn)題解決(@Autowired和@Resource注解的不同),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • spring-redis-session 自定義 key 和過(guò)期時(shí)間

    spring-redis-session 自定義 key 和過(guò)期時(shí)間

    這篇文章主要介紹了spring-redis-session 自定義 key 和過(guò)期時(shí)間,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Java獲取服務(wù)器IP及端口的方法實(shí)例分析

    Java獲取服務(wù)器IP及端口的方法實(shí)例分析

    這篇文章主要介紹了Java獲取服務(wù)器IP及端口的方法,結(jié)合實(shí)例形式分析了java針對(duì)客戶端及服務(wù)器端各種常見(jiàn)的信息操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2018-12-12
  • Mybatis數(shù)據(jù)批量插入如何實(shí)現(xiàn)

    Mybatis數(shù)據(jù)批量插入如何實(shí)現(xiàn)

    這篇文章主要介紹了Mybatis數(shù)據(jù)批量插入如何實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07

最新評(píng)論

巴彦县| 台北市| 襄樊市| 花莲县| 清水河县| 五峰| 天水市| 五原县| 吉木萨尔县| 兴国县| 蓝山县| 凤冈县| 会昌县| 山丹县| 舞阳县| 七台河市| 肥城市| 肥西县| 博客| 福泉市| 东明县| 永嘉县| 吉林省| 喀喇沁旗| 当雄县| 正镶白旗| 甘洛县| 治多县| 固阳县| 台前县| 五指山市| 鸡泽县| 盱眙县| 辰溪县| 舞阳县| 达州市| 灵台县| 南投市| 松潘县| 江北区| 顺义区|