zookeeper实现分布式锁Demo

本文探讨了在分布式环境下面临的锁操作问题,介绍了使用Zookeeper和Redis实现分布式锁的原理和区别。Zookeeper通过创建临时节点实现锁,而Redis则依赖于`setNX`命令。文中指出Redis实现效率更高但稳定性较差,而Zookeeper则相反。在Zookeeper中,非法停止可能导致短暂的死锁。示例代码展示了如何在Zookeeper上实现分布式锁的模板及其具体操作流程。
摘要由CSDN通过智能技术生成

  分布式锁的产生原因,单服务集群或者分布式开发的环境下,多个JVM无法进行锁操作。实现方式又Redis 或者zookeeper。

实现原理:在zookeeper上创建临时节点,因为只有一个系统可以创建成功,创建不成功的话会报不存在的错误。当一个拿到锁得系统执行完之后,.close()方法。即关闭临时节点。其它线程可以创建即拿到锁。这个过程利用事件通知zookeeper的handleDataDeleted当节点删除时触发事件,可以用于通知其它线程拿锁。

Redis和zk实现分布式锁的区别:Redis是nosql数据库本质上和数据库解决分布式相仿,本质通过创建变量方式,setNx。而zk是微服务协调工具,他是通过创建临时节点方式实现分布式锁控制。

Redis实现分布式锁,效率更高,稳定性查,zk是稳定性高,但是效率较Redis低。

zk如果遇到非法停止比如宕机的情况下,会出现一段事件死锁,因为临时节点销毁有反应时间。

具体实现代码:

首先创建一个 分布式锁接口

public interface FBSLock {

    // 获取锁
    public void getLock();

    // 释放锁
    public void unLock();
}


设计一个抽象模板ZookeeperAbstractLock

public abstract class ZookeeperAbstractLock implements FBSLock {
    // 集群连接地址
    protected String CONNECTION = "ip";
    // zk客户端连接
    protected ZkClient zkClient = new ZkClient(CONNECTION);
    // path路径
    protected String lockPath = "/zkLockpath";
    protected CountDownLatch countDownLatch = new CountDownLatch(1);

    public void getLock() {
                                  //1.连接ZKClient,  
        if (tryLock()) {
            System.out.println("####Zk获取锁成功######");
        } else {//获取失败
            waitLock();//等待
            getLock();//继续获取递归
        }
    }

    // 获取锁
    abstract boolean tryLock();

    // 等待锁 这里具体看业务逻辑 可以不等待,就相当于拿不到锁可以干点其它工作,能干点是点
    abstract void waitLock();

    public void unLock() {
        if (zkClient != null) {
            System.out.println("#######ZK释放锁#########");
            zkClient.close();
        }
    }

}

具体模板的实现方法:

public class ZookeeperDistrbuteLock extends ZookeeperAbstractLock {

    @Override
    boolean tryLock() {
        try {
            zkClient.createEphemeral(lockPath);
            return true;
        } catch (Exception e) {//回报节点存在错误
            return false;
        }

    }

    @Override
    void waitLock() {

        // 使用zk临时事件监听
        IZkDataListener iZkDataListener = new IZkDataListener() {

            public void handleDataDeleted(String path) throws Exception {//节点被删除
                if (countDownLatch != null) {
                    countDownLatch.countDown();//因为默认1.当处于0时位于await()的线程可以执行了
                }
            }

            public void handleDataChange(String arg0, Object arg1) throws Exception {//节点被修改这里来用不到

            }
        };
        // 注册事件通知
        zkClient.subscribeDataChanges(lockPath, iZkDataListener);
        if (zkClient.exists(lockPath)) {
            countDownLatch = new CountDownLatch(1);
            try {
                countDownLatch.await();
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        // 监听完毕后,移除事件通知,为了不影响程序效率
        zkClient.unsubscribeDataChanges(lockPath, iZkDataListener);
    }

}

具体的测试类:

public class OrderService implements Runnable {
    private OrderNumGenerator orderNumGenerator = new OrderNumGenerator();
    private FBSLock extLock = new ZookeeperDistrbuteLock();

    public void run() {
        getNumber();
    }

    public void getNumber() {
        try {
            extLock.getLock();
            String number = orderNumGenerator.getNumber();
            System.out.println("线程:" + Thread.currentThread().getName() + ",生成订单id:" + number);
        } catch (Exception e) {

        } finally {
            extLock.unLock();
        }
    }

    public static void main(String[] args) {
        System.out.println("多线程生成number");
        for (int i = 0; i < 100; i++) {
            new Thread(new OrderService()).start();
        }
    }

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值