分布式锁3: zk实现分布式锁4 使用临时顺序节点+watch监听+可重入(threadLocal)

一  zk实现分布式锁的可重入性

1.1 使用ThreadLocal属性

引入ThreadLocal线程局部变量保证zk分布式锁的可重入性。

1.2 关键代码说明

1.3 代码

1.3.1 初始化客户端

1.3.2 分布式锁代码

package com.atguigu.distributed.lock.config;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.zookeeper.*;

import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @ClassName: ZkDistriubtedTHLock
 * @Description: TODO
 * @Author: admin
 * @Date: 2024/01/06 16:07:04 
 * @Version: V1.0
 **/
public class ZkDistriubtedTHLock  implements Lock {
    private static final String ROOT_PATH = "/d-zk";
    private String path;
    private ZooKeeper zooKeeper;
    private static final ThreadLocal<Integer> THREAD_LOCAL = new ThreadLocal<>();
    public ZkDistriubtedTHLock(ZooKeeper zooKeeper,String lockName) {
        try {
            this.zooKeeper = zooKeeper;
            if (THREAD_LOCAL.get() == null || THREAD_LOCAL.get() == 0){
                this.path = zooKeeper.create(ROOT_PATH + "/" + lockName + "-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            }
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void lock() {
        Integer flag = THREAD_LOCAL.get();
        if (flag != null && flag > 0) {
            THREAD_LOCAL.set(flag + 1);
            return;
        }
        else{
            try {
                String preNode = getPreNode(path);
                // 如果该节点没有前一个节点,说明该节点时最小节点,放行执行业务逻辑
                if (StringUtils.isEmpty(preNode)){
                    THREAD_LOCAL.set(1);
                    return ;
                } else {
                    CountDownLatch countDownLatch = new CountDownLatch(1);
                    if (this.zooKeeper.exists(ROOT_PATH + "/" + preNode, new Watcher() {
                        @Override
                        public void process(WatchedEvent event) {
                            System.out.println("当前节点=="+path+" 前一个节点:"+ROOT_PATH + "/" + preNode);
                            countDownLatch.countDown();
                        }
                    }) == null) {
                        System.out.println("监听。。。");
                        THREAD_LOCAL.set(1);
                        return;
                    }
                    // 阻塞。。。。
                    countDownLatch.await();
                    System.out.println("wait。。。");
                    THREAD_LOCAL.set(1);
                    return;
                }


            } catch (Exception e) {
                e.printStackTrace();
                // 重新检查。是否获取到锁
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                lock();
            }
        }

    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }
    /**
     * 获取指定节点的前节点
     * @param path
     * @return
     */
    private String getPreNode(String path){

        try {
            // 获取当前节点的序列化号
            Long curSerial = Long.valueOf(StringUtils.substringAfterLast(path, "-"));
            // 获取根路径下的所有序列化子节点
            List<String> nodes = this.zooKeeper.getChildren(ROOT_PATH, false);

            // 判空
            if (CollectionUtils.isEmpty(nodes)){
                return null;
            }

            // 获取前一个节点
            Long flag = 0L;
            String preNode = null;
            for (String node : nodes) {
                // 获取每个节点的序列化号
                Long serial = Long.valueOf(StringUtils.substringAfterLast(node, "-"));
                if (serial < curSerial && serial > flag){
                    flag = serial;
                    preNode = node;
                }
            }

            return preNode;
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public void unlock() {
        try {
            THREAD_LOCAL.set(THREAD_LOCAL.get() - 1);
            if (THREAD_LOCAL.get() == 0) {
                this.zooKeeper.delete(path, 0);
                THREAD_LOCAL.remove();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

1.3.3 service

1.3.4 controller

1.4 测试

1.4.1 nginx多节点代理

1.服务

2.nginx

1.4.2 jemeter测试

3.jemeter

4.数据库前后

5.服务节点日志

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/297290.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java:Lambda表达式、方法引用

文章目录 1、Lambda表达式1.1 Lambda表达式体验1.2 Lambda表达式的省略形式1.3 Lambda表达式练习 2、方法引用体验3、方法引用符4、引用静态方法5、引用对象的实例方法6、引用类的实例方法7、引用构造方法8、引用数组的构造方法9、方法引用练习9.1 练习19.2 练习29.3 练习3 10、…

差分电路原理以及为什么输出电压要偏移

我们在使用放大器芯片的时候&#xff0c;除了对放大器芯片本身应用外&#xff0c;通常还需要搭建一些外围电路来满足放大器芯片的使用条件&#xff0c;最终满足应用的功能&#xff0c;下面通过一个差分电路来熟悉这些应用。 差分运算放大电路&#xff0c;对共模信号得到有效抑…

函数——系统函数2(c++)

这次主要就只有一个系统函数&#xff1a; &#xff08;注&#xff1a;a为变量名&#xff09; 名称 用法 用处 sqrt sqrt(a) 算出变量a的平方根 &#xff08;注&#xff1a;使用sqrt函数时&#xff0c;需要用到头文件 #i…

leetcode算法题之递归--综合练习(一)

此专题对我们之前所学的关于递归的内容进行一个整合&#xff0c;大家可以自行练习&#xff0c;提升自己的编码能力。 本章目录 1.找出所有子集的异或总和在求和2.全排列II3.电话号码的字母组合4.括号生成5.组合6.目标和7.组合总和8.字母大小写全排列9.优美的排列 1.找出所有子…

Python自动点击器

一、如何制作一个Python自动点击器&#xff1f; 当用户单击开始键时&#xff0c;代码将从键盘获取输入&#xff0c;并在用户单击退出键时终止自动点击器&#xff0c;自动点击器开始单击指针放置在屏幕上的任何位置。我们将在这里使用pynput模块。 二、什么是自动点击器&#…

FineBI实战(1):mysql实战案例简介

下面我通过案例来介绍FineBI的使用。 1 业务背景介绍 本案例围绕某个互联网小型电商的订单业务来开发。某电商公司&#xff0c;每天都有一些的用户会在线上采购商品&#xff0c;该电商公司想通过数据分析&#xff0c;查看每一天的电商经营情况。例如&#xff1a;电商公司的运…

并发(2)

目录 6.通常线程有哪几种使用方式&#xff1f; 7.基础线程机制有哪些&#xff1f; 8.线程的中断方式有哪些&#xff1f; 9.线程的互斥同步方式有哪些&#xff1f;如何比较和选择&#xff1f; 10.Synchronized可以作用在哪里&#xff1f; 6.通常线程有哪几种使用方式&#x…

Python基础知识总结3-面向对象进阶知识

面向对象三大特征介绍 继承子类扩展父类语法格式关于构造函数&#xff1a;类成员的继承和重写查看类的继承层次结构 object根类dir() 查看对象属性重写 __str__() 方法 多重继承MRO方法解析顺序super()获得父类定义多态特殊方法和运算符重载特殊属性 对象的浅拷贝和深拷贝组合_…

【提示学习论文五】Conditional Prompt Learning for Vision-Language Models论文原理及复现工作

Conditional Prompt Learning for Vision-Language Models 视觉语言模型的条件提示学习 文章介绍 这篇文章于2022年发表在CVPR&#xff08;Conference on Computer Vision and Pattern Recognition&#xff09;&#xff0c;作者是kaiyang.zhou, jingkang001, ccloy, ziwei.li…

PostgreSQL的常见错误和解决方法

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 在学习新的东西时&#xff0c;会犯很多的错误&#xff0c;会遇到很多坑。我们在填坑与犯错中不断进步成长。 以下是在学习pgsql中…

【QT】自定义代理类

目录 1 我们为什么要使用自定义代理类&#xff1f; 2 自定义代理类的基本设计要求 3 自定义代理的功能 4 基于QSpinBox的自定义代理类 5 自定义代理类的使用 1 我们为什么要使用自定义代理类&#xff1f; 传统的模型-视图框架可以让我们实现逻辑展示相分离&#xff0c;我们…

trino-435:dynamic catalog数据库存储代码实现

一、dynamic catalog数据库存储源码分析 dynamic catalog的实现主要涉及到两个类&#xff1a;CoordinatorDynamicCatalogManager、WorkerDynamicCatalogManager&#xff0c;这两个类的详细信息如下&#xff1a; 这两个类主要提供了对catalog的增删改查的方法。trino-435源码中…

C++补充内容--EasyX-UI界面

esay x 其他 地图打印(利用二维数组) 双缓冲 当我们绘制一张图 然后另一张图盖住前一张图的某个部分的时候 由于while的存在 会导致 两张图不停的闪烁 所以加入双缓冲可以解决这个问题 开启双缓冲 之后等待Flush或者End 才会进行图片的绘制 不然不会进行图片的绘制,这样就可…

docker拉取镜像提示 remote trust data does not exist for xxxxxx

1、How can I be sure that I am pulling a trusted image from docker 2、docker: you are not authorized to perform this operation: server returned 401. 以上两个问题可以试试以下解决办法 DOCKER_CONTENT_TRUSTfalse 本人是使用jenkins部署自己的项目到docker容器出现…

Linux基础——进程初识(二)

1. 对当前目录创建文件的理解 我们知道在创建一个文件时&#xff0c;它会被默认创建到当前目录下&#xff0c;那么它是如何知道当前目录的呢&#xff1f; 对于下面这样一段代码 #include <stdio.h> #include <unistd.h>int main() {fopen("tmp.txt", …

51单片机串行口相关知识

51单片机串行口相关知识 串行通信概念 计算机与外部通信方式就两种&#xff1a; 并行通信串行通信 两种通信方式的特点以及适用场景&#xff1a; 名称特点适用场景并行通信速度快&#xff0c;效率高&#xff0c;成本高适合短距离高速通信&#xff0c;如计算机内部各硬件之…

MySQL-DDL

DDL是数据定义语言&#xff0c;用来定义数据对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09; 数据库操作&#xff1a; 1.查询&#xff1a; 查询所有数据库&#xff1a;SHOW DATABASES; 查询当前数据库&#xff1a;SELECT DATABASE(); 2.创建&#xff1a; C…

性能分析与调优: Linux 使用 iperf3 进行TCP网络吞吐量测试

目录 一、实验 1.环境 2.TCP网络吞吐量的微观基准测试 二、问题 1.iperf参数有哪些 2.iperf如何二进制安装 一、实验 1.环境 &#xff08;1&#xff09;主机 表1-1 主机 主机架构组件IP备注prometheus 监测 系统 prometheus、node_exporter 192.168.204.18grafana监测…

BLE Mesh蓝牙组网技术详细解析之Model Layer模型层(八)

目录 一、什么是BLE Mesh Model Layer模型层&#xff1f; 二、SIG Model 2.1 模型概念 2.2 消息格式 2.3 开关模型 四、资料获取 一、什么是BLE Mesh Model Layer模型层&#xff1f; Models Layer的作用是定义了一些通用的或特定的模型&#xff0c;用于实现网络节点设备…

UE5 VR版增强输入初体验 官方模板学习

问题 我们传统的输入方式&#xff0c;是通过编辑器设置输入操作映射&#xff0c;然后BindAction和BindAxis绑定 这边插播一条增强输入知识点&#xff0c;参考知乎大佬文章 和增强输入的VR模板教学&#xff1a;如何使用VR模板在UE5中使用增强输入系统_哔哩哔哩_bilibili 实践操…