Zookeeper实现分布式锁

使用zookeeper实现分布式锁是分布式锁的实现方式的一种,相对于redis的实现,zookeeper的显现能够实现锁的获得顺序,不出现死锁等特点,关于zookeeper分布式锁的实现原理大致总结如下:

  • 客户端向zookeeper的某一个持久节点下注册临时有序节点
  • 获取该父节点下的所有临时有序节点,将自己刚才注册的临时节点与节点集合的第一个做比较,如果相同则表示自己是第一个注册也就是可以获得锁的,否则,注册比前置节点的监听事件,如果监听到前置节点被删除(即节点状态发生变化),则重新获取节点集合并进行比较

原理性的东西网上很多很全面,现在我贴下代码,做一个简陋的分布式锁的实现

1.下载zookeeper服务器到本地,启动

https://zookeeper.apache.org/releases.html#download

解压缩后,修改conf目录下的zoo_sample.cfg复制为zoo.cfg,修改里面的配置,没有/data和/log目录就新建一个吧
在这里插入图片描述
在bin目录下启动zk_server.cmd,然后启动zk_client.cmd客户端来验证下服务器是否已经启动成功

2.pom.xml引入zkclient的客户端jar包
<dependency>
    <groupId>com.101tec</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.11</version>
</dependency>
3.代码实现
package com.cw.zklock;
 
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.ZooKeeper;
 
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
 
public class ZookeeperLock{
 
    private ZkClient zkClient;
 
    private static final String ROOT_PATH="/lock";
 
    private String lockName;
    private String eNodeName;
    private String eNodeNo;
 
    public  ZookeeperLock(String lockName){
        zkClient = new ZkClient("localhost:2181",2000);
        //连接zk
        this.lockName =ROOT_PATH.concat("/").concat(lockName);
        //创建持久节点,该节点下后面创建临时有序节点
        if (!zkClient.exists(this.lockName)) {
            zkClient.createPersistent(this.lockName);
        }
    }
 
    public void lock(){
        for (;;) {
            if (eNodeName == null) {
                //创建临时有序节点
                eNodeName = zkClient.createEphemeralSequential(lockName.concat("/"), System.currentTimeMillis());
                eNodeNo=eNodeName.substring(eNodeName.lastIndexOf("/")+1);
            }
            List<String> children = zkClient.getChildren(lockName);
            //节点排序
            Collections.sort(children);
            if (children.size() > 0) {
                if (children.get(0).equals(eNodeNo)) {
                    //自己创建的就是第一个临时顺序节点,意味着拿到了锁
                    System.out.println(eNodeNo+"------------》获得了锁");
                    break;
                }else {
                    CountDownLatch latch = new CountDownLatch(1);
                    //获取改节点的前一个节点,注册监听事件,并等待
                   String prevPath= children.get(children.indexOf(eNodeNo)-1);
                    System.out.println(eNodeNo+"注册监听到------》"+prevPath);
                    zkClient.subscribeDataChanges(lockName.concat("/").concat(prevPath), new IZkDataListener() {
                        @Override
                        public void handleDataChange(String s, Object o) throws Exception {
                            latch.countDown();
                        }
 
                        @Override
                        public void handleDataDeleted(String s) throws Exception {
                            System.out.println(eNodeNo+"!!!!!!!!!!!被唤醒");
                            latch.countDown();
                        }
                    });
 
                    try {
                        System.out.println(eNodeNo+"??????????????开始等待");
                        latch.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }else {
                //异常情况
                System.out.println("子节点数据为空,抛出异常");
                throw new RuntimeException("获取锁异常");
            }
        }
    }
 
    public void unLock(){
        zkClient.delete(eNodeName);
        zkClient.close();
        System.out.println(eNodeNo+"********************释放了锁");
    }
 
    public static void main(String[] args) {
        //测试5个线程的争抢
        int threadCount=5;
        CountDownLatch latch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            Thread thread = new Thread(()->{
                try {
                    latch.await();
                    ZookeeperLock lock = new ZookeeperLock("lockTest");
                    lock.lock();
                    //模拟处理业务逻辑
                    Thread.sleep(1000+ new Random().nextInt(100)*10);
                    lock.unLock();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
            latch.countDown();
        }
 
 
    }
}

转自:https://www.cnblogs.com/manmanrenshenglu/p/12951321.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值