07-zk实现分布式锁

本文详细介绍了一种基于ZooKeeper实现的分布式锁方案。通过定义接口和抽象类模板,文章进一步介绍了两种具体实现:基于ZooKeeper异常的实现和基于监听的实现。这两种方法分别利用ZooKeeper的特性来确保在分布式环境中锁的一致性和原子性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、定义接口

  • 接口包含获取锁和释放锁
    public interface Lock {

    public void getLock();

    public void unLock();

}

二、流程模板

  • 在抽象类中使用模板方法定义好获取锁和释放锁的流程
    public abstract class ZookeeperAbstractLock implements Lock {

    private static final String ZookeeperConnectString = "192.168.xx.xx:2181";

    //创建zk连接
    protected ZkClient zkClient = new ZkClient(ZookeeperConnectString);
    protected static final String PATH = "/lock";
    protected static final String PATH2 = "/lock2";

    public void getLock() {
        if (tryLock()) {
            System.out.println("获取zk锁资源...");
        } else {
            //等待
            waitLock();
            //重新获取锁资源
            getLock();
        }
    }

    //获取锁资源
    abstract boolean tryLock();

    //等待
    abstract void waitLock();

}

三、实现

  • 子类继承抽象类,实现获取锁,等待锁和释放锁的方法即可。
基于zk异常的实现
    public class ZookeeperDistributeLock extends ZookeeperAbstractLock {

    private CountDownLatch countDownLatch = null;

    @Override
    boolean tryLock() {
        try {
            //创建临时节点,创建成功则返回
            zkClient.createEphemeral(PATH);
            return true;
        } catch (Exception e) {
            //e.printStackTrace();
            //System.out.println("获取锁失败...");
            return false;
        }
    }

    @Override
    void waitLock() {
        IZkDataListener listener = new IZkDataListener() {
            public void handleDataChange(String dataPath, Object data) throws Exception {

            }

            public void handleDataDeleted(String dataPath) throws Exception {
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };

        //注册事件
        zkClient.subscribeDataChanges(PATH, listener);
        if (zkClient.exists(PATH)) {
            countDownLatch = new CountDownLatch(1);
            try {
                //如果起那么的节点一直存在,自己就等着,直到前面的节点被删除,方法再返回,
                // 这里的await就相当于一直阻塞在这里等着,删除的动作在listen里面实现
                countDownLatch.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //删除监听
        zkClient.unsubscribeDataChanges(PATH, listener);

    }

    public void unLock() {
        if (zkClient != null) {
            zkClient.delete(PATH);
            zkClient.close();
            System.out.println("释放锁资源...");
        }
    }
}
基于监听的实现
    public class ZookeeperDistributeListenLock extends ZookeeperAbstractLock {

    private String beforePath;
    private String currentPath;
    private CountDownLatch countDownLatch = null;

    public ZookeeperDistributeListenLock() {
        if (!zkClient.exists(PATH2)) {
            zkClient.createPersistent(PATH2);
        }
    }

    @Override
    boolean tryLock() {
        //如果currentPath为空,则尝试加锁,第一次加锁赋值currentPath,并创建临时有序节点
        if (currentPath == null || currentPath.length() < 1) {
            currentPath = zkClient.createEphemeralSequential(PATH2 + '/', "lock");
        }
        //获取所有临时节点并且排序
        List<String> childrens = zkClient.getChildren(PATH2);
        Collections.sort(childrens);
        //如果自己是第一个节点,那么就认为自己获取到锁了
        if (currentPath.equals(PATH2 + '/' + childrens.get(0))) {
            return true;
        } else {
            //如果当前节点不是排名第一,那么就获取其前面一个节点,并在后面监听前面的节点
            int wz = Collections.binarySearch(childrens, currentPath.substring(7));
            beforePath = PATH2 + '/' + childrens.get(wz - 1);
        }
        return false;
    }

    @Override
    void waitLock() {
        IZkDataListener listener = new IZkDataListener() {
            public void handleDataChange(String dataPath, Object data) throws Exception {

            }

            public void handleDataDeleted(String dataPath) throws Exception {
                if (countDownLatch != null) {
                    countDownLatch.countDown();
                }
            }
        };
        //监听前面一个节点是否被删除,前面节点一旦被删除,自己就有机会获取锁了
        zkClient.subscribeDataChanges(beforePath, listener);
        if (zkClient.exists(beforePath)) {
            countDownLatch = new CountDownLatch(1);
            try {
                //如果起那么的节点一直存在,自己就等着,直到前面的节点被删除,方法再返回,
                // 这里的await就相当于一直阻塞在这里等着,删除的动作在listen里面实现
                countDownLatch.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //删除监听
        zkClient.unsubscribeDataChanges(beforePath, listener);
    }

    public void unLock() {
        if (zkClient != null) {
            zkClient.delete(currentPath);
            zkClient.close();
            System.out.println("释放锁资源...");
        }
    }
}

四、源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值