分布式鎖的一種實(shí)現(xiàn)

分布式鎖的三種實(shí)現(xiàn)方式
基于數(shù)據(jù)庫(kù)
  • 新建一張表,每次insert 一條記錄,利用唯一約束,釋放鎖刪除此記錄即可。
  • for update 利用行級(jí)鎖。
    強(qiáng)依賴數(shù)據(jù)庫(kù),一但數(shù)據(jù)庫(kù)不可用則系統(tǒng)不可用。
    一單獲取鎖失敗,則直接返回失敗,線程不會(huì)進(jìn)入等待隊(duì)列。
Redis

推薦redission,提供豐富的工具類,支持LUA腳本,支持spring框架等等(太多,大家可以度娘下)。
我見(jiàn)過(guò)很多的應(yīng)用中都是一種寫(xiě)法

jedis.set(key, value, "NX", "PX", expireTime);

這種寫(xiě)法有什么問(wèn)題?當(dāng)多個(gè)線程同時(shí)獲取鎖失敗時(shí),未獲取到鎖的線程依然不能進(jìn)入等待隊(duì)列,直接返回失敗,很多童鞋使用了強(qiáng)大的武器for循環(huán),而在redission中的lock就利用了redis的訂閱功能實(shí)現(xiàn)的線程的等待和通知,有興趣可以參考https://redisson.org/

zookeeper

今天這里主要分析zookeeper的實(shí)現(xiàn)方式和細(xì)節(jié),以幫助大家在應(yīng)用、以及在面試過(guò)程當(dāng)中能夠很好的理解和回答分布式鎖的實(shí)現(xiàn)過(guò)程。


image.png
  • DEMO
package com.jyly.mydubbo.zk;

import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;

/**
 * @author 咖啡爺爺
 *
 */
public class Lock {
    // 根節(jié)點(diǎn)目錄
    private String ROOT_LOCK = "/locks";
    CountDownLatch countDownLatch = null;
    ZkClient zkClient = null;
    final LockContext context = new LockContext();
    TreeSet<String> treeSet = null;
    public static String lockKey = "node";
    public Lock() {
        // init zkclient
        zkClient = new ZkClient("xxx.xxx.xxx.xx:2181");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    Lock lock = new Lock();
                    try {
                        if (lock.tryLock(lockKey)) {
                            System.out.println(Thread.currentThread().getName() + ">>>>獲取到鎖");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                        System.out.println(Thread.currentThread().getName() + ">>>>釋放鎖");
                    }
                }
            }).start();
        }
    }

    /**
     * 獲取分布式鎖
     * @param key
     * @return
     */
    public boolean tryLock(String key) {
        try {
            if (!zkClient.exists(ROOT_LOCK)) {
                zkClient.createPersistent(ROOT_LOCK);
            }
            String seq = zkClient.createEphemeralSequential(ROOT_LOCK.concat("/").concat(key), null);
            context.set(seq.substring(seq.lastIndexOf("/") + 1, seq.length()));
            if (isMinNode()) {
                return true;
            } else {
                addListenPreNode();
                countDownLatch = new CountDownLatch(1);
                for(;;) {
                    countDownLatch.await();
                    if(isMinNode()) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    // 判斷當(dāng)前是否是最小節(jié)點(diǎn)
    private boolean isMinNode() {
        treeSet = new TreeSet<>();
        for (String children : zkClient.getChildren(ROOT_LOCK)) {
            treeSet.add(children);
        }
        String minNode = treeSet.first();
        if (context.get().equals(minNode)) {
            return true;
        }
        return false;
    }

    /**
     *  添加監(jiān)聽(tīng)前驅(qū)節(jié)點(diǎn)
     */
    private void addListenPreNode() {
        zkClient.subscribeDataChanges(ROOT_LOCK.concat("/").concat(treeSet.lower(context.get())), new IZkDataListener() {
            public void handleDataChange(String arg0, Object arg1) throws Exception {
            }

            public void handleDataDeleted(String arg0) throws Exception {
                countDownLatch.countDown();
            }
        });
    }

    /**
     * 解鎖釋放節(jié)點(diǎn)
     */
    public void unlock() {
        zkClient.delete(ROOT_LOCK.concat("/").concat(context.get()));
    }
}

class LockContext {

    ThreadLocal<String> lockContext = new ThreadLocal<>();

    public String get() {
        return lockContext.get();
    }

    public void set(String seq) {
        lockContext.set(seq);
    }
}
  • zk里面的目錄結(jié)構(gòu)


    image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容