zookeeper入门篇之分布式锁
文章目录
- 前言
- 非公平锁
- 公平锁
前言
上一篇说过,zookeeper是一个类似文件系统的数据结构,每个节点都可以看做是一个文件目录,也就是说,我们所创建的节点是唯一的,那么分布式锁的原理就是基于这个来的。
代码仓库:https://gitee.com/LIRUIYI/test-zk.git
非公平锁
通过创建节点,并判断节点是否存在实现分布式锁。
- 创建临时节点,如
/lock
,临时节点是当出现异常,没有删除导致永久加锁的情况发生 - 当节点不存在,创建节点成功,也就意味着枷锁成功,如果创建失败,那么就是加锁失败
- 其他需要加锁的线程,监听
/lock
节点(get -w /lock),当节点/lock
删除后,zookeeper会通知到监听的节点
这种方式的加锁,不能保证需要加锁的线程能得到锁的概率一样,他们是随机的,有可能最先排队的加锁线程,到最后都不能得到锁,这就是非公平锁。
以原始客户端实现非公平锁:
这里zookeeper地址和上面不一样,因为之前是桥接模式,自动获取的,有时ip会变动,所以改NAT模式,设定了静态ip
package com.liry.zk;import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;/*** 分布式锁 - 非公平锁实现** @author ALI* @since 2022/12/25*/
@Slf4j
public class NonfairSyncLock {private static final String CONNECT_STR = "192.168.17.128:2181";private static final int TIME_OUT = 30000;private static final String LOCK_PATH = "/lock";private static ZooKeeper zookeeper = null;/*** 获取客户端*/public static ZooKeeper getClient() throws IOException, InterruptedException {synchronized (LOCK_PATH) {final CountDownLatch latch = new CountDownLatch(1);if (zookeeper == null) {Watcher startWatcher = watchedEvent -> {if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {log.info("与zookeeper建立连接");latch.countDown();}};zookeeper = new ZooKeeper(CONNECT_STR, TIME_OUT, startWatcher);latch.await();} else if (zookeeper.getState() != ZooKeeper.States.CONNECTED) {latch.await();}}return zookeeper;}/*** 加锁*/public void lock() {while (true) {if (tryLock()) {return;}// 加锁失败,增加监听,然后阻塞try {watch();} catch (Exception e) {throw new RuntimeException(e);}}}/*** 解锁*/public void unlock() {try {getClient().delete(LOCK_PATH, -1);} catch (Exception e) {throw new RuntimeException(e);}}/*** 尝试加锁*/private boolean tryLock() {try {getClient().create(LOCK_PATH, "lock".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);} catch (Exception e) {log.error("加锁失败");return false;}return true;}/*** 监听锁节点* 当节点存在时,线程阻塞,当节点不存在,直接退出监听*/private void watch() throws InterruptedException, KeeperException, IOException {CountDownLatch latch = new CountDownLatch(1);Watcher dataWatch = event -> {if (event.getType() == Watcher.Event.EventType.NodeDeleted) {latch.countDown();}};try {getClient().getData(LOCK_PATH, dataWatch, null);} catch (KeeperException.NoNodeException e) {// 当创建监听时,节点不存在,说明有线程解锁了,那么直接退出,监听步骤,去争抢锁return;}latch.await();}}
static int count = 0;public static void main(String[] args) throws InterruptedException {nonFairLock();}private static void nonFairLock() throws InterruptedException {NonfairSyncLock lock = new NonfairSyncLock();CountDownLatch latch = new CountDownLatch(1000);List<Thread> threadList = IntStream.range(0, 1000).mapToObj(d -> new Thread(() -> {lock.lock();count += 1;latch.countDown();lock.unlock();}, "线程-" + d)).collect(Collectors.toList());threadList.forEach(Thread::start);latch.await();System.out.println("最终结果应是1000:" + count);}
公平锁
相对于非公平锁的实现,这个方式较为复杂一点。
-
先创建一个根节点
/lock
-
再在
/lock
下创建临时有序节点,有序节点是因为所有需要加锁的节点需要按先来后到的顺序才能公平 -
然后每个有序节点都监听它的前一个节点,如图,当前一个节点被删除,表示解锁了,那么zookeeper会通知到监听的节点,也就是下一个需要加锁的线程
这个在curator中已经有实现了,分布式锁不局限于zookeeper,了解其原理就行
static int count = 0;public static void main(String[] args) throws InterruptedException {
// nonFairLock();fairLock();}private static void fairLock() throws InterruptedException {// 使用curator客户端RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);CuratorFramework client = CuratorFrameworkFactory.builder().connectString("92.168.17.128:2181").sessionTimeoutMs(3000).connectionTimeoutMs(3000).retryPolicy(retryPolicy).build();client.start();InterProcessMutex lock = new InterProcessMutex(client, "/lock");CountDownLatch latch = new CountDownLatch(1000);List<Thread> threadList = IntStream.range(0, 1000).mapToObj(d -> new Thread(() -> {try {lock.acquire();} catch (Exception e) {throw new RuntimeException(e);}count += 1;latch.countDown();try {lock.release();} catch (Exception e) {throw new RuntimeException(e);}}, "线程-" + d)).collect(Collectors.toList());threadList.forEach(Thread::start);latch.await();System.out.println("最终结果应是1000:" + count);}
相关文章:

zookeeper入门篇之分布式锁
文章目录 前言非公平锁公平锁 前言 上一篇说过,zookeeper是一个类似文件系统的数据结构,每个节点都可以看做是一个文件目录,也就是说,我们所创建的节点是唯一的,那么分布式锁的原理就是基于这个来的。 代码仓库&…...
leetcode解题思路分析(一百四十九)1297 - 1304 题
子串的最大出现次数 给你一个字符串 s ,请你返回满足以下条件且出现次数最大的 任意 子串的出现次数: 子串中不同字母的数目必须小于等于 maxLetters 。 子串的长度必须大于等于 minSize 且小于等于 maxSize 。 首先能想到的是从MinSize开始遍历查找&am…...

你的librosa和scikit-learn打架了吗?
被这个问题困扰好久!!!!!!!!!!!!!! 我的原来版本librosa0.7.1 和 scikit-learn1.3.1 一直拆了按,按…...

理解自动驾驶感知技术
理解自动驾驶感知技术 文章目录 什么是自动驾驶感知技术?自动驾驶感知技术的关键组成部分1. 雷达(Radar)2. 摄像头(Camera)3. 激光雷达(Lidar)4. 超声波传感器(Ultrasonic Sensors&a…...

一款简化Python自然语言处理的开源库
迷途小书童 读完需要 3分钟 速读仅需 1 分钟 1 简介 TextBlob 是一个 Python 库,用于处理文本数据的自然语言处理(NLP)任务。它提供了简单且易于使用的 API,使得对文本进行分析、情感分析、词性标注、名词短语提取等任务变得更加简…...

常用Redis界面化软件
对于Redis的操作,前期有过介绍【Centos 下安装 Redis 及命令行操作】。而在Redis的日常开发调试中,可使用可视化软件方便进行操作。 本篇主要介绍Redis可视化的两款工具:Redis Desktop Manager和AnotherRedisDesktopManager。 1、Redis Desk…...

电脑散热——液金散热
目录 1.简介 2.传统硅脂与液金导热区别 3.特点 4.优点 5.为什么液金技术名声不太好 6.使用方法 1.简介 凡是对于电脑基础硬件有所了解的人,都知道硅脂是如今高性能电脑设备中必不可少的东西。芯片表面和散热器接触面,虽然肉眼看上去是非常光滑的金属…...

多线程锁-synchronized字节码分析
从字节码角度分析synchronized实现 javap -c(v附加信息) ***.class 文件反编译 synchronized同步代码块 >>>实现使用的是monitorenter和monitorexit指令 synchronized普通同步方法 >>>调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置…...

SpringCloud学习笔记-Eureka的服务拉取
假设是OrderService里面拉取Eureka的服务之一User Service 1.依然需要在该服务里面引入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependenc…...

COLLABORATIVE DESIGNER FOR SOLIDWORKS® 新功能
共享和标注 优点:收件人在浏览器中访问共享文 件,无需安装3DEXPERIENCE 平台应用程序。 • 与 SOLIDWORKS 中来自您组织内部或外部的任何人无缝 共享您的设计。 • 直接将评论和标注附加到您的设计作品中,便于立即获得 反馈。 支持 SOLIDWO…...
AMD CPU 虚拟机安装 macos 系统的各虚拟机系统对比
软硬件环境: CPU:AMD R7 7735HS 8核16线程 显卡:AMD R680M 集显 内存:32GB DDR5 硬盘:2TB SSD Windows11 1、VMware Workstation 我用的是17 的版本,使用方便,对于macos 12及以下的安装在需要修改vmx 文…...
php实战案例记录(20)时间比较
在PHP中,有几种常见的方法可以进行时间比较。以下是其中的一些方法: 使用比较运算符:可以使用比较运算符(如小于"<“、大于”>“、小于等于”<“、大于等于”>“、等于”“、不等于”!"等)来比…...
web中缓存的几种方式
看了构建高性能的web站点一书,对其中的集中web缓存进行一个总结 1 应用程序实现的动态页面缓存 应用程序把动态文件生成的html文件缓存到文件服务器,以后用户请求动态文件,直接从文件服务器加载对应的静态缓存的html文件返回给用户ÿ…...

Stable Diffusion生成图片
画质 masterpiece,best quality,illustration,extremely detail CG unity 8k wallpaper,ultra-detailed,depth of field 杰作,最佳质量,插图,极度详细的8K壁纸,超高详细度,景深 画风 Chinese ink painting,water color…...

MySQL增删查改(进阶1)
一、数据库约束 约束:按照一定条件进行规范的做事; 表定义的时候,某些字段保存的数据需要按照一定的约束条件; 1.null约束 字段null:该字段可以为空;not null:该字段不能为空不指定的话就是…...

RabbitMQ-发布订阅模式和路由模式
接上文 RabbitMQ-工作队列 1 发布订阅模式 将之前的配置类内容都替换掉 Bean("fanoutExchange")public Exchange exchange(){//注意这里是fanoutExchangereturn ExchangeBuilder.fanoutExchange("amq.fanout").build();}Bean("yydsQueue1")publ…...

RabbitMQ-主题模式
接上文 RabbitMQ-发布订阅模式和路由模式 1 主题模式 #通配符 代表0个或多个。*通配符 代表 1个或多个 进行测试,修改配置文件 Configuration public class RabbitConfiguration {Bean("topicExchange") //这里使用预置的Topic类型交换机public Exchan…...
阅读文献小技巧
在科研中,文献的阅读是非常重要的一环。对于汇报论文的文献阅读,更是需要有一定的技巧。下面列出一些阅读汇报论文文献的技巧。 1.明确阅读目的和任务。在阅读每篇文献之前,需要明确阅读该文献的目的和任务,例如是否需要了解该领域的最新进展、寻找相关数据或案例等。是为…...
简易的贪吃蛇小游戏(以后或许会更新)C++/C语言
第一版: #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <windows.h>#define WIDTH 20 #define HEIGHT 20int gameOver; int score; int x, y; // 蛇头的坐标 int fruitX, fruitY; // 食物的坐标 int tailX[100], t…...
23云计算全国职业技能大赛容器云-容器编排
erp 2.2.1 容器化部署 MariaDB [0.5 分]2.2.2 容器化部署 Redis [0.5 分]2.2.3 容器化部署 Nginx [0.5 分]2.2.4 容器化部署 ERP[0.5 分]2.2.5 编排部署 ERP管理系统[1 分] 2.2.1 容器化部署 MariaDB [0.5 分] 编写 Dockerfile 文件构建 mysql 镜像,要求基于 centos…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...