zookeeper入门篇之分布式锁

news/2024/10/29 7:26:08/

文章目录

  • 前言
  • 非公平锁
  • 公平锁

前言

上一篇说过,zookeeper是一个类似文件系统的数据结构,每个节点都可以看做是一个文件目录,也就是说,我们所创建的节点是唯一的,那么分布式锁的原理就是基于这个来的。

代码仓库:https://gitee.com/LIRUIYI/test-zk.git

非公平锁

通过创建节点,并判断节点是否存在实现分布式锁。

  1. 创建临时节点,如/lock,临时节点是当出现异常,没有删除导致永久加锁的情况发生
  2. 当节点不存在,创建节点成功,也就意味着枷锁成功,如果创建失败,那么就是加锁失败
  3. 其他需要加锁的线程,监听/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);}

公平锁

相对于非公平锁的实现,这个方式较为复杂一点。

  1. 先创建一个根节点/lock

  2. 再在/lock下创建临时有序节点,有序节点是因为所有需要加锁的节点需要按先来后到的顺序才能公平

  3. 然后每个有序节点都监听它的前一个节点,如图,当前一个节点被删除,表示解锁了,那么zookeeper会通知到监听的节点,也就是下一个需要加锁的线程

    image-20221225132210169

这个在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);}

http://www.ppmy.cn/news/1142032.html

相关文章

React Hooks—— context hooks

什么是Hooks Hooks从语法上来说是一些函数。这些函数可以用于在函数组件中引入状态管理和生命周期方法。 React Hooks的优点 简洁 从语法上来说&#xff0c;写的代码少了上手非常简单 基于函数式编程理念&#xff0c;只需要掌握一些JavaScript基础知识与生命周期相关的知识不…

【数据结构--八大排序】之快速排序

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

【MySQL】Linux 中 MySQL 环境的安装与卸载

文章目录 Linux 中 MySQL 环境的卸载Linux 中 MySQL 环境的安装 Linux 中 MySQL 环境的卸载 在安装 MySQL 前&#xff0c;我们需要先将系统中以前的环境给卸载掉。 1、查看以前系统中安装的 MySQL rpm -qa | grep mysql2、卸载这些 MySQL rpm -qa | grep mysql | args yum …

笔记--总线舵机YB-SD15M--stm32

文章目录 前言一、官方文档的理解1.发送格式2.命令地址 二、控制文件1.c2.h 文件 前言 使用stm32控制这个总线舵机。 舵机为总线舵机。一定要配合控制板一起用&#xff0c;不然只使用stm32无法控制。 一、官方文档的理解 1.发送格式 发送格式如下&#xff0c;其中的指令类型…

从0开始python学习-28.selenium 需要图片验证的登录

url https://test.com/login driver.get(url) # 获取登录页面需要输入账号密码进行模拟登录操作 user driver.find_element(By.XPATH,//*[id"login"]/div[2]/div/form[2]/div[2]/div/div/input).send_keys(username) pwd driver.find_element(By.XPATH,//*[id&qu…

SpringBoot vue云办公系统

SpringBoot vue云办公系统 系统功能 云办公系统 登录 员工资料管理: 搜索员工 添加编辑删除员工 导入导出excel 薪资管理: 工资账套管理 添加编辑删除工资账套 员工账套设置 系统管理: 基础信息设置 部门管理 职位管理 职称管理 权限组管理 操作员管理 开发环境和技术 开发语…

react函数式组件的useEffect

useEffect 1.useEffect的介绍 引用官网的一句话就是&#xff1a;useEffect 就是一个 Effect Hook&#xff0c;给函数组件增加了操作副作用的能力。&#xff08;你之前可能已经在 React 组件中执行过数据获取、订阅或者手动修改过 DOM。我们统一把这些操作称为“副作用”&…

计算机竞赛 深度学习火车票识别系统

文章目录 0 前言1 课题意义课题难点&#xff1a; 2 实现方法2.1 图像预处理2.2 字符分割2.3 字符识别部分实现代码 3 实现效果4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 图像识别 火车票识别系统 该项目较为新颖&#xff0c;适…