zookeeper分布式锁处理

逻辑:

  1. 接收到请求后,在/locks节点下创建一个临时顺序节点
  2. 判断自己是不是当前节点下最小的节点,是,获取锁,不是,对前一个节点进行监听
  3. 获取到锁,处理完业务后,delete节点释放锁,然后下面的节点将收到通知,重复第二步判断。

编码实现:
新建类DistributedLock,通过构造方法进行逻辑处理

public DistributedLock() throws IOException,KeeperException,InterruptedException
建立zookeeper连接
//1.建立连接
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
    @Override
    public void process(WatchedEvent watchedEvent) {

    }
});
//等待zk正常连接后,往下走程序
countDownLatch.await();
判断根节点/locks是否存在:
//2.判断根节点/locks是否存在
Stat stat = zk.exists("/locks",false);

if (stat == null){
    //创建一下根节点
    zk.create("/locks","locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}

加锁:

//创建对应的临时带序号的节点
try {
    String currentMode = zk.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
    //判断当前节点是不是最小序号节点,如果是获取到锁,如果不是,监听他序号前一个节点
    List<String> children = zk.getChildren("/locks", false);
    //如果children只有一个值,那就直接获得锁,如果有多个节点,需要判断,
    if (children.size()==1){

    }else {
        Collections.sort(children);
        //获取节点名称
        String thisNode = currentMode.substring("/locks/".length());
        //通过seq-00000000获取该节点在children集合的位置
        int index = children.indexOf(thisNode);

        //判断
        if (index == -1){
            System.out.println("数据异常");
        }else if (index == 0){
            //就一个节点,可以获取锁
            return;
        }else {
            //需要监听 他前一个节点变化
            waitPath = "/locks/"+children.get(index - 1);
            zk.getData(waitPath,true,null);
            //等待监听
            waitLatch.await();
            return;
        }
    }
} catch (KeeperException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
}

解锁: 就是删除节点

//删除节点
try {
    zk.delete(currentMode,-1);
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (KeeperException e) {
    e.printStackTrace();
}

整体代码就写完了,如下:

public class DistributedLock {

    private String connectString = "192.168.59.128:2181,192.168.59.129:2181,192.168.59.130:2181";
    private int sessionTimeout = 2000;
    private ZooKeeper zk;

    private CountDownLatch countDownLatch = new CountDownLatch(1);
    private CountDownLatch waitLatch = new CountDownLatch(1);
    private String waitPath;
    private String currentMode;

    public DistributedLock() throws IOException,KeeperException,InterruptedException{
        //1.建立连接
        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                //countDownLatch表示如果连接上zk,可以释放
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
                    countDownLatch.countDown();
                }

                //waitLatch也需要释放
                if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)){
                    waitLatch.countDown();
                }
            }
        });

        //等待zk正常连接后,往下走程序
        countDownLatch.await();
        //2.判断根节点/locks是否存在
        Stat stat = zk.exists("/locks",false);

        if (stat == null){
            //创建一下根节点
            zk.create("/locks","locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
        }
    }

    //3.对zk加锁
    public void zkLock(){
        //创建对应的临时带序号的节点
        try {
            currentMode = zk.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            //判断当前节点是不是最小序号节点,如果是获取到锁,如果不是,监听他序号前一个节点
            List<String> children = zk.getChildren("/locks", false);
            //如果children只有一个值,那就直接获得锁,如果有多个节点,需要判断,
            if (children.size()==1){

            }else {
                Collections.sort(children);
                //获取节点名称
                String thisNode = currentMode.substring("/locks/".length());
                //通过seq-00000000获取该节点在children集合的位置
                int index = children.indexOf(thisNode);

                //判断
                if (index == -1){
                    System.out.println("数据异常");
                }else if (index == 0){
                    //就一个节点,可以获取锁
                    return;
                }else {
                    //需要监听 他前一个节点变化
                    waitPath = "/locks/"+children.get(index - 1);
                    zk.getData(waitPath,true,null);
                    //等待监听
                    waitLatch.await();
                    return;
                }
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

    }
}

编写DistributedLockTest类进行功能测试

/**
 * 测试模拟两个线程获取锁
 */
public class DistributedLockTest {

    public static void main(String[] args) throws InterruptedException, KeeperException, IOException {

        final DistributedLock distributedLock1 = new DistributedLock();

        final DistributedLock distributedLock2 = new DistributedLock();

        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    distributedLock1.zkLock();
                    System.out.println("线程1启动,获取到锁");
                    Thread.sleep(5*1000);
                    distributedLock1.unZkLock();
                    System.out.println("线程1释放锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    distributedLock2.zkLock();
                    System.out.println("线程2启动,获取到锁");
                    Thread.sleep(5*1000);
                    distributedLock2.unZkLock();
                    System.out.println("线程2释放锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

启动:日志打印
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值