zookeeper实现分布式锁(学习zookeeper)

文章提供了一个使用Zookeeper实现分布式锁的Java代码,通过创建有序节点并监控前一个节点的状态来实现加锁和解锁。代码适用于学习目的,不适用于生产环境,注意其非严谨的逻辑。测试代码展示了多线程环境下对共享资源的同步操作。
摘要由CSDN通过智能技术生成

1、手写这种重复轮子的原因

学习zookeeper的过程中,用来熟悉zookeeper的操作及原理

2、上代码

该代码不可用在生产环境,因为是学习用途,逻辑并不谨慎(重要、重要、重要)

实现逻辑,代码注释已经写得比较清楚了,就不赘述了(我很懒)

package com.example.demo22.zookeerper;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.List;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Collectors;

/**
 * @author anxu
 * @date 2023/2/9 9:23
 */
public class Lock {
    private static final String NODE_PATH = "/anxu/lock";
    private static final String PARENT_NODE_PATH = "/anxu";

    /**
     * 当前获取到锁的Node(排在最前面的NODE)
     */
    private String currentNode;
    /**
     * 当前获取锁的线程
     */
    private Thread currentThread;

    /**
     * zooKeeper对象
     */
    private ZooKeeper zooKeeper;

    public Lock() {
        this.zooKeeper = ZookeeperFactory.getZookeeper();
    }

    /**
     * 加锁
     *
     * @return
     */
    public boolean lock() throws KeeperException, InterruptedException {
        /**
         * 加锁流程
         * 1、申请在 /anxu节点下创建有序节点 lock
         * 2、查看 /anxu下的所有子节点,判断自己的节点是否是最前面的
         *  2。1、如果是,提示获取锁成功
         *  2.2、如果不是,则watch排在自己前面的节点,并park自己,等待唤醒
         *
         */
        //检查是否为当前线程, 支持 可重入锁
        if (Thread.currentThread() == this.currentThread) {
            //获取重入次数
            int count = Integer.valueOf(new String(zooKeeper.getData(this.currentNode, true, new Stat())));
            //获取版本号
            int version = zooKeeper.exists(this.currentNode, true).getVersion();
            //更新重入次数
            zooKeeper.setData(this.currentNode, String.valueOf(count + 1).getBytes(), version);
            return true;
        }
        //创建有序节点
        String node = zooKeeper.create(NODE_PATH, "1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        return this.tryLock(node);
    }

    private boolean tryLock(String node) throws KeeperException, InterruptedException {
        //查看父节点下的所有有序子节点 自己是否排在最前面
        List<String> children = zooKeeper.getChildren(PARENT_NODE_PATH, true);
        String minNode = getMinNode(children);
        //检查自己是否是排在最前面的节点
        if (node.equals(minNode)) {
            //设置当前占有锁的线程
            this.currentThread = Thread.currentThread();
            this.currentNode = node;
            return true;
        }

        //获取排在自己前面的节点名称(路径)
        String preNode = this.getPreNode(children, node);
        //记录当前线程,便于唤醒
        Thread currentThread = Thread.currentThread();
        //在前一个节点上注册watch监听
        zooKeeper.getData(preNode, watchedEvent -> {
            if (watchedEvent.getType() == Watcher.Event.EventType.NodeDeleted) { //监听节点删除
                LockSupport.unpark(currentThread); //如果前一个节点被删除,说明锁已经放开,我们唤醒线程继续尝试获取锁
            }
        }, new Stat());
        LockSupport.park();
        return tryLock(node);
    }

    /**
     * 解锁
     *
     * @return
     */
    public boolean unlock() throws KeeperException, InterruptedException {
        //获取重入次数
        int count = Integer.valueOf(new String(zooKeeper.getData(this.currentNode, true, new Stat())));
        count = count - 1;
        //获取版本号
        int version = zooKeeper.exists(this.currentNode, true).getVersion();
        if (count > 0) {
            //更新 重入次数
            zooKeeper.setData(this.currentNode, String.valueOf(count).getBytes(), version);
        } else {
            //删除节点
            String currentNode = this.currentNode;
            this.currentNode = null;
            this.currentThread = null;
            zooKeeper.delete(currentNode, version);
        }
        return true;
    }

    /**
     * 获取集合中的排在最前面的节点
     *
     * @param list
     * @return
     */
    private String getMinNode(List<String> list) {
        return PARENT_NODE_PATH + "/" + list.stream().sorted().collect(Collectors.toList()).get(0);
    }

    /**
     * 获取节点的前一个节点
     *
     * @param list
     * @param currentNode
     * @return
     */
    private String getPreNode(List<String> list, String currentNode) {
        List<String> sortList = list.stream().map(it -> PARENT_NODE_PATH + "/" + it).sorted().collect(Collectors.toList());
        int index = sortList.indexOf(currentNode);
        return sortList.get(index - 1);
    }
}

3、运行效果

测试代码

package com.example.demo22.zookeerper;

import org.apache.zookeeper.KeeperException;

/**
 * @author anxu
 * @date 2023/1/10 17:21
 */
public class Test {
    private static int sum = 0;
    public static void main(String[] args) throws InterruptedException, KeeperException {
        Lock lock = new Lock();
        Thread.sleep(1000);
        for (int j = 0; j < 3; j++) {
            new Thread(() -> {
                for (int k = 0; k < 1000; k++) {
                    try {
                        lock.lock();
                        sum++;
                    } catch (KeeperException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            lock.unlock();
                        } catch (KeeperException e) {
                            e.printStackTrace();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
        Thread.sleep(20000);
        System.out.println(sum);
    }
}

执行结果

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值