Zookeeper(五)Zookeeper实现分布式锁

一、简介

  在日常开发过程中,大型的项目一般都会采用分布式架构,那么在分布式架构中若需要同时对一个变量进行操作时,可以采用分布式锁来解决变量访问冲突的问题,最典型的案例就是防止库存超卖,当然还有其他很多的控制方式如数据库乐观锁、redis实现分布式锁等,这篇文章我们讨论一下怎么使用ZooKeeper来实现分布式锁。

二、尝试写一个分布式锁

开发思路【实现一个公平锁】

  在前面的文章中我们学习了ZooKeeper节点的概念,实现分布式锁的基本思路也是基于对节点的监听与操作从而实现的。

  • 1、创建一个父节点/lockDemo,并对父节点设置监听事件,实际加锁的对象为父节点下的子节点【临时节点】。
  • 2、当用户请求进来时,会在/lockDemo节点下创建临时顺序节点
  • 3、此时创建的节点会判断自己是不是/lockDemo下最小的节点。
    • A、是最小的,获得锁
    • B、不是最小的,对前一个节点添加watch监听事件
  • 4、获得锁的请求在执行完业务代码之后,要释放锁【delete临时顺序节点】,此时后面的节点【对这个节点进行监听的后一个节点】会收到通知,并执行前面的获取锁逻辑。

  PS:由于非公平锁会带来“羊群效应”,性能不高,所以这里就不考虑了。

  PS:羊群效应:每次只有一个线程加锁成功,剩下的请求都在监听等待抢锁,当释放锁时这些监听的线程又一窝蜂的去抢锁,循环往复,会给zookeeper带来很大的效能压力。

三、代码实现

  Curator客户端已经帮我们封装得很好了,我们并不需要按照开发思路中一步一步来写代码,只需要用现成的API即可,方便快捷且可靠!

  由于环境配置、pom依赖、获取zk连接的工具类ZookeeperClientUtil等代码,在前面的文章已经写过了,这里就不重复写了,只放分布式锁的实现思路代码:

/**
 * 分布式锁测试类
 * -- InterProcessMutex  : Curator封装的分布式锁操作对象
 *
 * @author 有梦想的肥宅
 * @date 2021/08/18
 */
public class CuratorLockDemo {

    public static void main(String[] args) {
        //1、创建两个分布式锁用于测试
        InterProcessMutex lock1 = new InterProcessMutex(ZookeeperClientUtil.getCuratorFramework(), "/lockDemo");
        InterProcessMutex lock2 = new InterProcessMutex(ZookeeperClientUtil.getCuratorFramework(), "/lockDemo");

        //2、定义线程执行内容
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //3、测试可重入性
                    lock1.acquire();
                    System.out.println("线程1 获取到锁");

                    lock1.acquire();
                    System.out.println("线程1 重入时获取到锁");

                    Thread.sleep(5 * 1000);

                    lock1.release();
                    System.out.println("线程1 释放重入的锁");

                    lock1.release();
                    System.out.println("线程1 释放锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock2.acquire();
                    System.out.println("线程2 获取到锁");

                    lock2.acquire();
                    System.out.println("线程2 重入时获取到锁");

                    Thread.sleep(5 * 1000);

                    lock2.release();
                    System.out.println("线程2 释放重入的锁");

                    lock2.release();
                    System.out.println("线程2 释放锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

}
原文地址:https://www.cnblogs.com/riches/p/15159544.html