zoukankan      html  css  js  c++  java
  • 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();
        }
    
    }
  • 相关阅读:
    上传和下载附件功能
    C#小常识,持续更新..
    动态添加HTML表单控件,无(runat="server")
    Excel技巧 持续更新..
    JS函数集锦 持续更新..
    JS 函数 检验输入是否为数字类型,正整数
    存储过程 游标 事例
    Sql 查询语句中的类型转换
    shell 计数脚本
    centos 获取文件的创建时间
  • 原文地址:https://www.cnblogs.com/riches/p/15159544.html
Copyright © 2011-2022 走看看