Redis学习——Redisson 分布式锁集成及其简单使用

文章目录

  • 引言
  • 1. Redisson概述
    • 1.1 Redisson的基本概念
    • 1.2 Redisson的主要功能
    • 1.3 Redisson的优点
  • 2. 开发环境
  • 3. Redisson的安装与配置
    • 3.1 添加依赖
    • 3.2 配置Redisson
  • 4. 使用Redisson
    • 4.1 可重入锁
      • 4.1.1 可重入锁的概念
      • 4.1.2 可重入锁的实现原理
      • 4.1.3 简单使用
          • 锁的获取和释放
    • 4.2 公平锁
      • 4.2.1 公平锁的概念
      • 4.2.2 公平锁的实现原理
      • 4.2.3 简单使用
    • 4.3 读写锁
      • 4.3.1 读写锁的概念
      • 4.3.2 读写锁的实现原理
      • 4.3.3 简单使用
    • 4.4 联锁
      • 4.4.1 联锁的概念
      • 4.4.2 联锁的实现原理
      • 4.4.3 简单使用
  • 5. WatchDog机制
  • 6. 总结

在这里插入图片描述

引言

在分布式系统中,经常需要对共享资源进行并发访问控制,以确保数据的一致性和完整性。分布式锁是一种用于在分布式环境中控制对共享资源访问的机制,它可以保证在同一时刻只有一个客户端能够访问某些特定资源。

1. Redisson概述

1.1 Redisson的基本概念

Redisson是一个基于Redis的Java客户端,它不仅提供了对Redis的基础操作支持,还封装了许多高级功能,如分布式锁、分布式集合、分布式队列等。Redisson的设计目标是简化分布式系统的开发,提高开发效率和系统的可维护性。

下载 (6)

1.2 Redisson的主要功能

  1. 分布式锁:支持可重入锁、公平锁、读写锁、红锁等多种分布式锁机制,保证分布式环境下的资源访问控制。比如:在电商系统中,防止超卖现象;在订单系统中,防止同一订单被多次处理。
  2. 分布式集合:提供分布式Set、List、Map等集合类型,支持高并发环境下的数据操作。
  3. 分布式队列:支持分布式阻塞队列、延迟队列等,适用于任务调度和消息传递场景。
  4. 分布式对象:提供分布式AtomicLong、AtomicDouble、CountDownLatch、Semaphore等对象,简化分布式系统的开发。
  5. 分布式服务:支持分布式执行器、分布式调度器等服务,增强分布式系统的功能。

1.3 Redisson的优点

Redisson是一个基于Redis的Java客户端,提供了许多高级特性和分布式数据结构。相比其他Redis客户端,Redisson的优势在于:

  • 简洁易用:提供了丰富的API,简化了分布式编程的复杂性。
  • 高可用性:支持多种Redis部署模式,包括单节点、主从复制和集群模式。
  • 分布式对象:提供了分布式锁、分布式集合、分布式队列等高级数据结构,便于在分布式环境中使用。
  • 自动续期:Redisson的Watchdog机制可以自动续期分布式锁,避免锁超时问题。

2. 开发环境

  • JDK版本:JDK 17
  • Spring Boot版本:Spring Boot 3.2.2
  • Redis版本:5.0.14.1
  • 构建工具:Maven

3. Redisson的安装与配置

3.1 添加依赖

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.24.3</version>
</dependency>

3.2 配置Redisson

配置参考文档:2. Configuration · redisson/redisson Wiki (github.com)

添加配置类RedissonConfig

/*** Redisson配置类,用于配置Redisson客户端。*/
@Configuration
public class RedissonConfig {/*** 创建并配置RedissonClient Bean。* * @return 配置好的RedissonClient实例*/@Beanpublic RedissonClient redissonClient() {// 创建Redisson配置对象Config config = new Config();// 配置单节点模式config.useSingleServer()// 设置Redis服务器地址.setAddress("redis://127.0.0.1:6379")// 设置Redis服务器密码.setPassword("123321")// 设置连接池大小.setConnectionPoolSize(64)// 设置最小空闲连接数.setConnectionMinimumIdleSize(24)// 设置空闲连接超时时间(毫秒).setIdleConnectionTimeout(10000)// 设置连接超时时间(毫秒).setConnectTimeout(10000)// 设置命令等待超时时间(毫秒).setTimeout(3000)// 设置命令重试次数.setRetryAttempts(3)// 设置命令重试间隔时间(毫秒).setRetryInterval(1500);// 创建并返回RedissonClient实例return Redisson.create(config);}
}

4. 使用Redisson

官方wiki文档:8. Distributed locks and synchronizers · redisson/redisson Wiki (github.com)

中文版wiki文档(已经有5年没有更新了,不建议看):8. 分布式锁和同步器 · redisson/redisson Wiki (github.com)

4.1 可重入锁

4.1.1 可重入锁的概念

可重入锁(Reentrant Lock)是一种允许同一个线程多次获取同一把锁的锁机制。也就是说,当一个线程已经持有某个锁时,它可以再次获取该锁而不会被阻塞。这种锁机制能够避免死锁问题,并简化锁的使用。

可重入锁的主要特点是:

  • 同一线程可多次获取:同一个线程可以多次获取同一把锁,而不会被阻塞。
  • 计数器维护:可重入锁内部维护一个计数器,每次获取锁时计数器加1,每次释放锁时计数器减1,当计数器为0时,锁才真正被释放。

4.1.2 可重入锁的实现原理

可重入锁的实现通常依赖于一个计数器和一个持有锁的线程标识。当一个线程第一次获取锁时,计数器加1,并记录持有锁的线程标识。当同一个线程再次获取锁时,只需将计数器加1,而不会阻塞线程。当线程释放锁时,计数器减1,当计数器为0时,锁才真正被释放,并允许其他线程获取锁。

在Redisson中,可重入锁的实现基于Redis的原子操作和Lua脚本。Redisson通过维护一个计数器和持有锁的线程标识,实现了可重入锁的功能。

4.1.3 简单使用

import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;/*** 服务类示例*/
@RequiredArgsConstructor
@Service
public class XXXXService {private final RedissonClient redissonClient; /*** 使用 Redisson 可重入锁执行任务*/public void performTaskWithLock() {// 获取可重入锁对象,指定锁的名称RLock lock = redissonClient.getLock("myLock"); try {// 尝试获取锁,参数分别是:获取锁的最大等待时间,锁自动释放时间,时间单位,返回值为是否获取锁成功boolean isLock = lock.tryLock(1, 10, TimeUnit.SECONDS); // 判断获取锁成功if (isLock) { try {System.out.println("执行业务"); // 在这里编写需要进行锁保护的业务逻辑} finally {// 释放锁lock.unlock(); }} else {// 获取锁失败,可以进行相应的处理,例如记录日志或返回错误信息System.err.println("获取锁失败!"); }} catch (InterruptedException e) {// 处理中断异常throw new RuntimeException(e); }}
}

在上述代码中,我们使用redissonClient.getLock("myLock")获取一个分布式锁对象,然后使用lock.tryLock()方法尝试获取锁,并在任务完成后释放锁。

锁的获取和释放
  • 获取锁:使用RLock对象的tryLock()lock()方法来获取锁。tryLock()方法允许设置等待时间和锁的自动释放时间。
  • 释放锁:使用RLock对象的unlock()方法来释放锁。确保在finally块中释放锁,以避免死锁。

4.2 公平锁

4.2.1 公平锁的概念

公平锁(Fair Lock)是一种确保锁的获取顺序与请求顺序相同的锁机制。即先请求锁的线程优先获取锁,后请求的线程只能在前面的线程释放锁后才能获取锁。这种机制可以避免“饥饿”现象,确保每个线程都能公平地获取锁。

4.2.2 公平锁的实现原理

公平锁的实现通常依赖于一个队列来记录请求锁的顺序。每次有线程请求锁时,会将其添加到队列中,当锁被释放时,从队列中按照请求顺序依次唤醒等待的线程。

在Redisson中,公平锁的实现基于Redis的有序集合(Sorted Set)和Lua脚本。每次请求锁时,线程会被添加到一个有序集合中,并按照时间戳排序。当锁被释放时,按照有序集合中的顺序依次唤醒等待的线程。

4.2.3 简单使用

public void performTaskWithFairLock() {// 1. 获取公平锁对象RLock fairLock = redissonClient.getFairLock("myFairLock");try {// 2. 尝试获取锁boolean isLock = fairLock.tryLock(1, 10, TimeUnit.SECONDS); // 3. 判断是否获取到锁if (isLock) { try {System.out.println("获得公平锁,正在执行任务...");// 执行任务} finally {// 4. 释放锁fairLock.unlock(); System.out.println("释放公平锁。");}} else {System.out.println("无法获取公平锁。");}} catch (InterruptedException e) {e.printStackTrace();}
}

4.3 读写锁

4.3.1 读写锁的概念

读写锁(Read-Write Lock)是一种允许多个读操作同时进行,但写操作必须独占的锁机制。读写锁分为两种锁:读锁和写锁。

  • 读锁:允许多个线程同时获取读锁,只要没有线程持有写锁。读锁之间是共享的。
  • 写锁:只允许一个线程获取写锁,并且在写锁持有期间,其他线程不能获取读锁或写锁。写锁是独占的。

读写锁的主要目的是提高并发性和性能。在读多写少的场景下,读写锁可以显著提高系统的并发处理能力。

4.3.2 读写锁的实现原理

读写锁的实现通常依赖于两个锁:一个读锁和一个写锁。读锁允许多个线程同时获取,而写锁只允许一个线程获取。在获取写锁时,需要确保没有线程持有读锁或写锁。

在Redisson中,读写锁的实现基于Redis的原子操作和Lua脚本。Redisson通过两个键来分别控制读锁和写锁,并使用Lua脚本确保锁操作的原子性。

4.3.3 简单使用

public void performTaskWithReadWriteLock() {// 1. 获取读写锁对象RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("myReadWriteLock");// 2. 从读写锁对象中分别获取读锁和写锁RLock readLock = readWriteLock.readLock();RLock writeLock = readWriteLock.writeLock();try {// 3. 尝试获取读锁if (readLock.tryLock(10, 60, TimeUnit.SECONDS)) { try {System.out.println("获取读锁,正在执行读任务...");// 执行读任务} finally {// 4. 释放读锁readLock.unlock(); System.out.println("释放读锁。");}}// 5. 尝试获取写锁if (writeLock.tryLock(10, 60, TimeUnit.SECONDS)) { try {System.out.println("获取写锁,正在执行写任务...");// 执行写任务} finally {// 6. 释放写锁writeLock.unlock();System.out.println("释放写锁。");}}} catch (InterruptedException e) {e.printStackTrace();}
}

4.4 联锁

4.4.1 联锁的概念

联锁(MultiLock)是一种允许将多个锁关联在一起,实现“全部获取”或“全部释放”的锁机制。

  • 全部获取: 只有当所有参与联锁的锁都被成功获取后,才算成功获取联锁。
  • 全部释放: 释放联锁时,会自动释放所有参与联锁的锁。

联锁适用于需要同时获取多个资源的场景,例如分布式事务中需要锁定多个数据表。

4.4.2 联锁的实现原理

Redisson 的联锁基于 RedissonMultiLock 对象实现。RedissonMultiLock 对象可以将多个 RLock 对象关联在一起,并提供 tryLock()unlock() 方法来统一管理这些锁。

在调用 tryLock() 方法时,RedissonMultiLock 会尝试依次获取所有参与联锁的锁。如果所有锁都获取成功,则返回 true,否则释放已经获取到的锁,并返回 false

在调用 unlock() 方法时,RedissonMultiLock 会自动释放所有参与联锁的锁,无论这些锁是否被当前线程持有。

4.4.3 简单使用

public void performTaskWithMultiLock() {// 获取多个锁对象RLock lock1 = redissonClient.getLock("lock1");RLock lock2 = redissonClient.getLock("lock2");RLock lock3 = redissonClient.getLock("lock3");// 创建联锁对象RLock multiLock = redissonClient.getMultiLock(lock1, lock2, lock3);try {// 尝试获取联锁,等待 10 秒if (multiLock.tryLock(10, TimeUnit.SECONDS)) {try {System.out.println("获取联锁成功,正在执行任务...");// 执行需要所有锁的任务} finally {// 释放联锁multiLock.unlock();System.out.println("释放联锁。");}} else {System.out.println("获取联锁失败。");}} catch (InterruptedException e) {e.printStackTrace();}
}

代码分析:

  1. 获取多个锁对象: 首先,获取需要参与联锁的多个 RLock 对象。
  2. 创建联锁对象: 使用 redissonClient.getMultiLock(lock1, lock2, lock3) 创建一个 RLock 对象,并将之前获取的多个锁对象作为参数传入。
  3. 尝试获取联锁: 调用 multiLock.tryLock(10, TimeUnit.SECONDS) 尝试获取联锁,最多等待 10 秒。
  4. 执行任务: 如果成功获取联锁,则执行需要所有锁保护的任务。
  5. 释放联锁: 最后,在 finally 块中调用 multiLock.unlock() 释放联锁,这会自动释放所有参与联锁的锁。

5. WatchDog机制

想象一下,我们正在进行一场激烈的拔河比赛。我们队好不容易抓住了绳子,眼看就要赢了,结果突然有人手滑,绳子就被对方抢走了!

在分布式系统中,获取锁就好像抓住这根拔河绳。Redisson 分布式锁的租约时间就好像我们抓住绳子的时间。如果在租约时间内,我们没有完成任务,锁就自动释放了,其他线程就有机会获取锁,这就像拔河比赛中我们手滑绳子被抢走一样,可能会导致数据不一致的问题。

为了避免这种情况发生,Redisson 提供了 Watch Dog 机制,就像我们队伍里安排了一个“观察员”。这位观察员会每隔一段时间关注我们是否还抓着绳子,如果发现我们快坚持不住了,就会及时提醒我们,让我们重新握紧绳子,并延长我们抓住绳子的时间。

具体来说,Redisson 的Watch Dog 机制会在 Redisson 实例被关闭前,不断的延长锁的有效期,也就是说,如果一个拿到锁的线程一直没有完成逻辑,那么看门狗会帮助线程不断的延长锁超时时间,锁不会因为超时而被释放。

**开启方式:**在获取锁的时候,不能指定leaseTime或者只能将leaseTime设置为-1,这样才能开启看门狗机制。

public void test() throws Exception {RLock lock = redissonClient.getLock("myLock");// 方式一: 不停重试,直到获取锁成功,具有 Watch Dog 自动延期机制,默认续约时间为 30 秒lock.lock(); // 方式二: 尝试获取锁 10 秒,获取成功返回 true,否则返回 false,具有 Watch Dog 自动延期机制,默认续约时间为 30 秒boolean res1 = lock.tryLock(10, TimeUnit.SECONDS); // 方式三:  尝试获取锁 10 秒,如果获取成功,则持有锁,否则抛出异常,leaseTime 为 10 秒,不会自动续约try {lock.lock(10, TimeUnit.SECONDS); } catch (InterruptedException e) {// 处理异常}// 方式四: 尝试获取锁 100 秒,如果获取成功,则持有锁 10 秒,leaseTime 为 10 秒,不会自动续约boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS); Thread.sleep(40000L);lock.unlock();
}

6. 总结

在本文中,我们简要介绍了Redisson及其优势,介绍了如何在Spring Boot项目中集成Redisson。通过代码示例展示了基本的分布式锁用法,以及高级用法如公平锁、可重入锁、读写锁和联锁。除此之外我们还简要介绍了Redisson 的Watch Dog 机制,希望本文对大家有所帮助😊。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在线疫苗预约小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;工作人员管理&#xff0c;管理员管理&#xff0c;用户管理&#xff0c;疫苗管理&#xff0c;论坛管理&#xff0c;公告管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;公告&#xff0c;疫苗&…

【FreeRTOS】空闲任务

目录 空闲任务及其钩子函数介绍使用钩子函数的前提 实际操作任务如何退出&#xff1f;IDLE函数 空闲任务及其钩子函数 介绍 空闲任务(Idle任务)的作用之一&#xff1a;释放被删除的任务的内存。 除了上述目的之外&#xff0c;为什么必须要有空闲任务? 这是一个良好的程序&…

fastapi登录功能

fastapi登录功能 1、登录参数必须是OAuth2PasswordRequestForm类型 因为在swagger UI中的登录使用的表单传账号和密码这个类依赖python-multipart&#xff0c;使用前需安装 pip install python-multipart2、示例代码 login_router.post("", summary"登录&qu…

Linux基础 - DHCP 动态管理主机地址

目录 零. 简介 一. 部署 dhcpd 二. 提高安全性 零. 简介 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff0c;动态主机配置协议&#xff09;是一种网络协议&#xff0c;用于动态地为主机分配 IP 地址、子网掩码、默认网关、DNS 服务器等网络配置信息。 其主要…

鸿蒙 HarmonyOs 动画效果 快速入门

一、理论 1.1 animation属性 名称参数类型必填描述durationnumber否设置动画时长&#xff0c;默认值&#xff1a;1000&#xff0c;单位&#xff1a;毫秒temponumber否动画播放速度。数值越大&#xff0c;速度越快&#xff0c;默认为1curvestring | Curve否 设置动画曲线。 默…

ubuntu 23 连接正点imx6ull的uboot

由于使用ubuntu23&#xff0c;无法连接正点的imx6ull的uboot&#xff0c;因为这个uboot里面的nfs是v2&#xff0c;ubuntu23内核是6.5不支持uboot v2。配置/etc/default/nfs-kernel-server sudo vim /etc/default/nfs-kernel-server 更改以下参数&#xff1a; RPCNFSDCOUNT"…

C语言--vs使用调试技巧

1.什么是bug? 1.产品说明书中规定要做的事情&#xff0c;而软件没有实现。 2.产品说明书中规定不要做的事情&#xff0c;而软件确实现了。 3.产品说明书中没有提到过的事情&#xff0c;而软件确实现了。 4.产品说明书中没有提到但是必须要做的事情&#xff0c;软件确没有实…

vue3中使用弹幕组件vue-danmaku

1、最开始使用的是vue3-marquee&#xff0c;后面发现一直有一个bug无法解决&#xff0c;就是鼠标hover到第一个弹幕上字体就会变粗&#xff0c;已经提了issue给作者&#xff0c;但是目前还未答复&#xff0c;所以就换了方案。 地址如下&#xff1a; https://github.com/megasa…

stable-diffusion-webui-colab搭建SadTalker由图生成视频人

在这里选择一个stable-diffusion-webui-colab ​​​​​​​​​GitHub - camenduru/stable-diffusion-webui-colab: stable diffusion webui colab 这里我选择是&#xff1a; https://colab.research.google.com/github/camenduru/stable-diffusion-webui-colab/blob/main…

【Python时序预测系列】基于LSTM实现多输入多输出单步预测(案例+源码)

这是我的第312篇原创文章。 一、引言 单站点多变量输入多变量输出单步预测问题----基于LSTM实现。 多输入就是输入多个特征变量 多输出就是同时预测出多个标签的结果 单步就是利用过去N天预测未来1天的结果 二、实现过程 2.1 读取数据集 dfpd.read_csv("data.csv&qu…

CSS-实例-div 水平居中 垂直靠上

1 需求 2 语法 3 示例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>表格水平居中、垂直靠上示例…

AI 上车的一些随想

​最近一直在做AI大模型上车的战略企划工作&#xff0c;听了好多供应商的宣讲&#xff0c;自己也查阅了大量书籍、资料。信息输入呈现爆炸性增长&#xff0c;受限于专业知识水平&#xff0c;仅能在应用层面上有所思考。纯个人观点&#xff0c;仅供参考。 车自古以来都是移动工…

2024年06月CCF-GESP编程能力等级认证Scratch图形化编程四级真题解析

本文收录于《Scratch等级认证CCF-GESP图形化真题解析》专栏,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 一、单选题(共 10 题,每题 2 分,共 30 分) 第1题 小杨父母带他到某培训机构给他报名参加 CCF 组织的 GESP 认证考试的第 1 级,那他可以选择的认证语言有几…

前端面试题(基础篇十四)

一、DOMContentLoaded 事件和 Load 事件的区别&#xff1f; 当初始的 HTML 文档被完全加载和解析完成之后&#xff0c;DOMContentLoaded 事件被触发&#xff0c;而无需等待样式表、图像和子框架的加载完成。 Load 事件是当所有资源加载完成后触发的。 二、简述一下你对 HTML 语…

机器学习 中数据是如何处理的?

数据处理是将数据从给定形式转换为更可用和更理想的形式的任务&#xff0c;即使其更有意义、信息更丰富。使用机器学习算法、数学建模和统计知识&#xff0c;整个过程可以自动化。这个完整过程的输出可以是任何所需的形式&#xff0c;如图形、视频、图表、表格、图像等等&#…

HarmonyOS开发实战:JSON组件使用方式-API

JSON类组件 模块介绍JSONValue提供eftool中的JSON相关对象的类型定义JSONObject提供类Java的JSON对象的系列方法以及相互转换JSONArray提供类Java的JSON数组的系列方法以及相互转换JSONArrayList提供类Java的JSON数组的系列方法以及相互转换JSONUtil提供JSON转换一系列判断方法…

理想汽车提出3DRealCar:首个大规模3D真实汽车数据集

理想提出3DRealCar&#xff0c;这是第一个大规模 3D 实车数据集&#xff0c;包含 2500 辆在真实场景中拍摄的汽车。我们希望 3DRealCar 可以成为促进汽车相关任务的宝贵资源。 理想汽车提出3DRealCar&#xff1a;首个大规模3D真实汽车数据集! 我们精心策划的高质量3DRealCar数…

全球点赞第一起名大师颜廷利:是金子总会“花光”的

在物质世界的繁华背后&#xff0c;隐藏着一个深刻的真理&#xff1a;有形之物的分享会逐渐减少&#xff0c;而无形之物的传递却能不断增值。金钱、货币、银两这些商业领域的实体&#xff0c;往往激发出人类对更多财富的渴望和对资源枯竭的恐惧。这种恐惧源于资源的有限性&#…

【数据结构】(C语言):二叉搜索树

二叉搜索树&#xff1a; 树不是线性的&#xff0c;是层级结构。基本单位是节点&#xff0c;每个节点最多2个子节点。有序。每个节点&#xff0c;其左子节点都比它小&#xff0c;其右子节点都比它大。每个子树都是一个二叉搜索树。每个节点及其所有子节点形成子树。可以是空树。…