Zookeeper实现简单分布式锁

Zookeeper实现简单分布式锁

转自https://blog.csdn.net/weixin_45271492/article/details/118489843?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165983947116782425191019%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165983947116782425191019&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-2-118489843-null-null.nonecase&utm_term=%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81&spm=1018.2226.3001.4450

实现步骤

  1. 使用zk的临时节点和有序节点,每个线程获取锁就是在zk创建一个临时有序的节点,比如在/locks目录下。

  2. 创建节点成功后,获取/locks目录下的所有临时节点,再判断当前线程创建的节点是否是所有的节点的序号最小的节点

  3. 如果当前线程创建的节点是所有节点序号最小的节点,则认为获取锁成功。

  4. 如果当前线程创建的节点不是所有节点序号最小的节点,则对节点序号的前一个节点添加一个事件监听。

比如当前线程获取到的节点序号为/locks/seq-00000003,然后所有的节点列表为[/locks/seq-00000001,/locks/seq-00000002,/locks/seq-00000003],则对/locks/seq-00000002这个节点添加一个事件监听器。

如果锁释放了,会唤醒下一个序号的节点,然后重新执行第3步,判断是否自己的节点序号是最小。

比如/locks/seq-00000001释放了,/locks/seq-00000002监听到事件,此时节点集合为[/locks/seq-00000002,/locks/seq-00000003],则/locks/seq-00000002为最小序号节点,获取到锁。

流程图如下所示
在这里插入图片描述

代码

public class DistributedLock {
    private final int sessionTimeOut=2000;
    private final ZooKeeper zk;
    //根节点
    private final String LOCK="/locks";
    //同步工具类,构造传入计数值,当计数达到0,释放所有等待的线程
    private CountDownLatch connectLatch=new CountDownLatch(1);
    private CountDownLatch waitLatch=new CountDownLatch(1);
    //监听上一个节点的路径
    private String waitPath;
    //当前节点
    private String currentNode;

    public DistributedLock(String connectString) throws IOException, InterruptedException, KeeperException {
        //获取连接
        zk=new ZooKeeper(connectString, sessionTimeOut, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                //连接上zk,释放
                if(event.getState()==Event.KeeperState.SyncConnected){
                    //计数-1
                    connectLatch.countDown();
                }
                if(event.getType()==Event.EventType.NodeDeleted && event.getPath().equals(waitPath)){
                    waitLatch.countDown();
                }
            }
        });
        //等待zk正常连接后,程序往下执行
        //在计数器为0之前一直等待
        connectLatch.await();
        Stat stat = zk.exists(LOCK, false);
        //判断根节点 /locks 是否存在,不存在就创建根节点
        if(stat==null){
            //创建下一个根节点
            zk.create(LOCK,"locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
        }
    }

    //加锁
    public void lock() {
        try {
            //创建对应的临时带序号节点
            currentNode= zk.create(LOCK + "/seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            //判断当前要创建的节点是否是序号最小的节点,如果是获取到锁,不是则监听前一个节点
            List<String> children = zk.getChildren(LOCK, false);
            //如果children只有一个值,说明就是自己,直接获取锁;如果有多个,需要判断谁最小
            if(children.size()==1){
                return;
            }else{
                Collections.sort(children);
                //获取节点名称 seq-00000000
                String thisNode = currentNode.substring((LOCK+"/").length());
                //通过 seq-00000000获取该节点在children集合的位置
                int index = children.indexOf(thisNode);
                if(index==0){
                    //说明只有一个节点,获取锁
                    return;
                }else{
                    //需要监听前一个节点的变化
                    waitPath=LOCK+"/"+children.get(index-1);
                    zk.getData(waitPath,true,new Stat());
                    //等待监听
                    waitLatch.await();
                    return;
                }
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //解锁
    public void unlock(){
        try {
            //删除节点
            zk.delete(currentNode,-1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值