Redisson 分布式锁 - RLock、RReadWriteLock、RSemaphore、RCountDownLatch(配置、使用、原理)

目录

前言

Redisson 分布式锁

环境配置

1)版本说明

2)依赖如下

3)配置文件如下

4)项目配置

RLock

1)使用方式

2)加锁解释

3)加锁时手动设置时间

4)加锁时,到底要不要手动设置过期时间?(最佳实践)

RReadWriteLock

1)使用方式

2)加锁原理

RSemaphore

1)使用方式

2)信号原理

RCountDownLatch

1)使用方式

2)原理解释


前言


前面讲过一篇 Redisson 分布式锁的底层原理,而这篇文章着重实战,因此对原理不清楚的,可以看看我之前的文章:http://t.csdnimg.cn/LU5ED

 

Redisson 分布式锁


环境配置

1)版本说明

  • SpringBoot:3.2.5
  • Redisson:3.25.0

 

2)依赖如下

<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-data-redis</artifactId>-->
<!--        </dependency>--><!--redisson 依赖整合了 StringRedisTemplate,因此 data redis 依赖就可以删除了--><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.25.0</version></dependency>

 

3)配置文件如下

spring:  data:redis:host: env-baseport: 6379

Ps:实际上还有一种配置方式就是自己配置 RedissonClient 的 Bean,注入给容器

 

4)项目配置

分布式锁带来的效果演示,会通过 jmeter 来进行测试.  服务这边会先通过 网关,在负载均衡到集群的实例上.  因此这里我们来配置一下集群的每个实例信息.

这里为了方便观察,准备了两个实例:

RLock

1)使用方式

如果使用的 Redisson 的 Boot Starter 依赖的话,只需要在 yml 按照本文配置,然后在需要的地方注入 RedissionClient 即可使用(非 Boot Starter 依赖需要自己配置 RedissonClient).

@RestController 
@RequestMapping("/product/lock")
class Test(private val redisson: RedissonClient
) {@GetMapping("/test1")fun test1(): String {//1.获取一把锁,只要名字一样,就是同一把锁val lock: RLock = redisson.getLock("my-lock")//2.加锁lock.lock()try {println("加锁成功,执行业务..." + Thread.currentThread().id)Thread.sleep(10000) //模拟耗时任务} catch (e: Exception) {e.printStackTrace()} finally {//3.解锁lock.unlock()println("解锁成功!" + Thread.currentThread().id)}return  "ok!"}}

 Ps:除此之外,还有 redisson 的 ReentrantLock 中还提供了 tryLock() 方法有以下两种重载方式

  • boolean tryLock():尝试加锁,如果当前锁被占用,则直接放弃并返回 false.
  • boolean tryLock(long time, TimeUnit unit):如果当前锁被占用,则会等待,知道到达我们设置的过期时间 time 还没拿到锁,就放弃并返回 false.

 

2)加锁解释

a)RLock 就类似于 JUC 中的 ReentrantLock,是一个可重入锁(同一个线程对同一个资源连续加锁两次不会死锁).

b)加锁实际上就是在 redis 上添加了一个 key-value,并且默认加锁的过期时间为 30s,如下图:

c)如果业务处理实践比默认加锁时间长怎么办?这里会有一个看门狗机制,只要拿到锁,就会开启一个定时任务,每隔 10s 就会自动续约.  因此不用担心业务时间长的问题.  

d)如果代码还没有执行到解锁,程序就挂了,会不会死锁?不会的,锁是有默认的过期时间,即使没有执行到解锁逻辑,锁也会自动删除.(这里我自己测试了一下,貌似新版的 Redisson 中会检测程序是否挂了,如果挂了,就会把这个锁立即删除掉)

3)加锁时手动设置时间

a)使用如下:

b)注意:

如果我们手动指定了过期时间,那么即使业务没有执行完,也不会自动续约.  也就是说,无论如何,到期自动解锁.

4)加锁时,到底要不要手动设置过期时间?(最佳实践)

a)最佳实践:

使用 lock.lock(30, TimeUnit.SECONDS) 手动设置 30s 过期时间.  

b)原因:

如果我们手动设置过期时间,就省掉了续约的操作(有一定的开销).

再者,真的会有某一个业务逻辑需要执行 30s 的时间么?如果真的有,这个程序大概率是出问题了.  到了 30s 后解锁,反而还避免了 “死等” 问题.

RReadWriteLock

1)使用方式

a)写操作(写锁)

    @GetMapping("/write")fun write(): String {val rwLock: RReadWriteLock = redisson.getReadWriteLock("rw-lock")var result = ""//1.获取写锁val wLock = rwLock.writeLock()//2.写操作用写锁,读操作用读锁wLock.lock()try {Thread.sleep(10000)result = UUID.randomUUID().toString()redisTemplate.opsForValue().set("uuid", result)} catch (e: Exception) {e.printStackTrace()} finally {wLock.unlock()}return "ok! uuid: $result"}

b)读操作(读锁)

    @GetMapping("/read")fun read(): String {val rwLock: RReadWriteLock = redisson.getReadWriteLock("rw-lock")var result: String? = ""//1.获取读锁val rLock = rwLock.writeLock()//2.写操作用写锁,读操作用读锁rLock.lock()try {result = redisTemplate.opsForValue().get("uuid")} catch (e: Exception) {e.printStackTrace()} finally {rLock.unlock()}return "ok! uuid: $result"}

Ps:读锁,写锁 这里也额外提供了 tryLock() 方法,来尝试加锁(原理上面讲过)

2)加锁原理

读写锁保证了读操作和读操作之间不会加锁,而读操作和其他任何操作都会加锁.   使得在读多写少的业务场景中,效率大大提升.

Redission 提供的 ReadWriteLock 也是这个原理:

  • 读 + 读: 读操作和读操作之间不会出现脏数据问题,因此相当于无锁,只会在 redis 中记录当前读锁,他们都会同时加锁成功.
  • 写 + 读:如果先写,此时紧接着又进行读操作,可能出现脏数据的问题,因此会阻塞等待写锁释放.
  • 写 + 写:写操作和写操作之间可能出现脏数据问题,因此也是阻塞等待.
  • 读 + 写:由于你读的时候,另一个线程又来写,也会出现脏数据的问题,因此也必须要阻塞等待读锁释放.

RSemaphore

1)使用方式

    @GetMapping("/park")fun park(): String {//这里的 RSemaphore 就相当于是一个停车场,刚开始的没有车位(初始信号量为 0)val park: RSemaphore = redisson.getSemaphore("park")//1.获取一个信号,相当于占了一个停车位,车位 - 1park.acquire()//2.执行业务//...return "park ok!"}@GetMapping("/go")fun go(): String {val park = redisson.getSemaphore("park")//1.释放一个信号,相当于让出了一个车位,车位 + 1park.release()return "go ok!"}

Ps:这里也有一个额外的方法 tryAcquire(),尝试申请资源 

2)信号原理

a)redisson.getSemaphore("park") 这里实际上就是在 redis 上添加一个 key-value,key 就是我们自定义的 "park" 字符串,value 就是信号量,初始情况下为 0. 

b)情况分析:

情况一:刚开始的时候如果 线程A 进行 acquire(),由于信号量为 0,只能阻塞等待.  接着如果有 线程B 进行 release(),就会释放一个信号量,也就是信号量 + 1,此时 线程A 发现有一个信号来了,他就直接消费掉了

情况二:刚开始的时候如果 线程A 进行 release(),此时信号量 + 1,总共信号量为 1,接着如果有线程B 来进行 acquire() ,就会直接消费掉这个信号,此时信号量 - 1,总共信号量为 0.

Ps:信号量也可以做分布式限流

RCountDownLatch

1)使用方式

例如有 5 个选手比赛,要求所有选手到达终点之后才可以宣布比赛结束.

    @GetMapping("/match")fun match(): String {val match = redisson.getCountDownLatch("match")//1.设置计数器的初始值为 5 (想象成有 5 名选手赛跑)match.trySetCount(5)//2.等待所有资源全被消费 (等待 5 名选手全部跑完)match.await()return "比赛结束!"}@GetMapping("/gogogo/{id}")fun gogogo(@PathVariable("id") id: Long): String {val match = redisson.getCountDownLatch("match")//1.计数器 - 1 (一名选手到达终点)match.countDown()return "选手 $id 号到达终点!"}

2)原理解释

a)redisson.getCountDownLatch("match") 这里实际上就是给 redis 存了一个 key-value,key 就是我们自定义的 "match" 字符串,value 就是计数器.

b)match.trySetCount(5) 就是给这个计数器设置了一个初始值为 5.

c)match.await() 会一直阻塞住,直到计数器的值减为 0.

d)match.countDown() 让计数器 - 1.

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

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

相关文章

揭秘FL Studio21.2.8中文版一键解锁音乐创作新境界!

在音乐制作的广阔天地里&#xff0c;随着技术的不断进步和数字音频工作站&#xff08;DAW&#xff09;软件的普及&#xff0c;越来越多的音乐爱好者和专业制作人开始涉足音乐创作的奇妙旅程。其中&#xff0c;FL Studio以其强大的功能、直观的操作界面和丰富的音色资源&#xf…

LED驱动IC:HC2106,1W升压型DC/DC白光LED驱动器HC2106系列,供应给大功率白光LED灯提供能源、恒流源

LED驱动IC&#xff1a; HC2106&#xff1a;1W升压型DC/DC白光LED驱动器HC2106系列 概述&#xff1a;HC2106系列芯片是针对LED应用设计的PFM 控制模式的开关型DC/DC 升压恒流芯片&#xff0c;通过外接电阻可使输出电流值恒定在0mA&#xff5e;500mA。 HC2106可以给一个、多个…

算法004:盛水最多的容器

这道题比较简单&#xff0c;使用双指针。 要求的是最大面积&#xff0c;对于一个水桶&#xff08;水杯来说&#xff09;&#xff0c;面积的算法是固定的&#xff0c;就是底乘以高。 在这个题中&#xff0c;我们把左边的位置设为left&#xff0c;右边的位置设为right&#xff…

一个月飙升 9k star!打破常规的 git 客户端

作为一名程序员&#xff0c;想必大家每天都要使用 git 来管理自己的代码吧。有些大佬喜欢使用命令行来进行 git 的操作&#xff0c;有些新入门的小白程序员则比较喜欢使用各种 git 客户端来可视化的管理代码&#xff0c;而有些程序员则喜欢使用 IDE 中集成的 git 功能来做代码的…

力扣234. 回文链表

给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true # Definition for singly-linked list. # c…

R语言探索与分析17-CPI的分析和研究

一、选题背景 CPI&#xff08;居民消费价格指数&#xff09;作为一个重要的宏观经济指标&#xff0c;扮演着评估通货膨胀和居民生活水平的关键角色。在湖北省这个经济活跃的地区&#xff0c;CPI的波动对于居民生活、企业经营以及政府宏观经济政策制定都具有重要的影响。因此&a…

打造卓越任务调度体系:实用攻略与技巧解析

写这篇文章&#xff0c;想和大家从头到脚说说任务调度&#xff0c;希望大家读完之后&#xff0c;能够理解实现一个任务调度系统的核心逻辑。 1 Quartz Quartz 是一款 Java 开源任务调度框架&#xff0c;也是很多 Java 工程师接触任务调度的起点。 下图显示了任务调度的整体流…

基于STC12C5A60S2系列1T 8051单片机实现一主单片机与一从单片机相互发送数据的RS485通信功能

基于STC12C5A60S2系列1T 8051单片机实现一主单片机与一从单片机相互发送数据的RS485通信功能的RS485通信功能 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列1T 8051单片机串…

zdppy_api 中间件请求原理详解

单个中间件的逻辑 整体执行流程&#xff1a; 1、客户端发起请求2、中间件拦截请求&#xff0c;在请求开始之前执行业务逻辑3、API服务接收到中间件处理之后的请求&#xff0c;和数据库交互&#xff0c;请求数据4、数据库返回数据5、API处理数据库的数据&#xff0c;然后给客户…

【第十一课】空间数据基础与处理——属性数据管理

一、前言 Arcgis分析离不开两大主体数据&#xff0c;一是空间&#xff0c;二是经济属性。在运用 Aecgis 进行分析时&#xff0c;经常会碰到一些涉及多要素的属性更改或填写&#xff0c; 如果按照普通的方法&#xff0c;每个属性进行修改或填写的话&#xff0c;工作量是很大的&…

Apache OFBiz 路径遍历导致RCE漏洞复现(CVE-2024-36104)

0x01 产品简介 Apache OFBiz是一个电子商务平台,用于构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式电子商务类应用系统。是美国阿帕奇(Apache)基金会的一套企业资源计划(ERP)系统。该系统提供了一整套基于Java的Web应用程序组件和工具。 0x02 漏洞概…

【深入理解计算机系统第3版】补码加法

感觉这部分有点难&#xff0c;所以稍微整理记一下。 抱歉中英混合&#xff0c;来回切换输入法真的很折磨人。 负溢出 正常 正溢出 以4位补码加法为例&#xff0c;理解下表(书中P64) 补码最大值Tmax 2^3 - 1 7, 补码最小值Tmin -2^3 -8 xyz x yz z mod 2^4zU2Tw(z)溢…

超燃混剪热门视频素材去哪里找? 爆款超燃网站合集分享

在今天的数字时代&#xff0c;短视频已成为传播信息和个人表达的主流方式。无论你是混剪爱好者还是自媒体创作者&#xff0c;掌握如何获取和利用高质量的视频素材是关键。本文将介绍几个顶级的视频素材网站&#xff0c;包括国内外的平台&#xff0c;帮助你创建引人入胜的视频作…

【数据结构】二叉搜索树--BST,Binary Search Tree

文章目录 二叉搜索树1. 二叉搜索树的概念2. 二叉搜索树的接口2.1 查找非递归查找递归查找 2.2 中序遍历2.3 插入非递归插入递归插入 2.4 删除非递归删除递归删除 3. 二叉搜索树的应用key搜索模型kv搜索模型 5. oj题 二叉搜索树 1. 二叉搜索树的概念 二叉搜索树又称二叉排序树…

12-学生们参加各科测试的次数(高频 SQL 50 题基础版)

12-学生们参加各科测试的次数 -- 学生表中&#xff0c;id是唯一的&#xff0c;将他作为主表 -- CROSS JOIN产生了一个结果集&#xff0c;该结果集是两个关联表的行的乘积 -- 2行表,与3行表使用cross join,得到2*36行数据 select st.student_id, st.student_name,su.subject_na…

Vxe UI vxe-upload vue上传组件,显示进度条的方法

vxe-upload vue 上传组件 查看官网 https://vxeui.com 显示进度条很简单&#xff0c;需要后台支持进度就可以了&#xff0c;后台实现逻辑具体可以百度&#xff0c;这里只介绍前端逻辑。 vue 上传附件 相关参数说明&#xff0c;具体可以看文档&#xff1a; multiple 是否允许…

现代密码学-国密算法

商用密码算法种类 商用密码算法 密码学概念、协议与算法之间的依赖关系 数字签名、证书-公钥密码、散列类算法 消息验证码-对称密码 &#xff0c;散列类 安全目标与算法之间的关系 机密性--对称密码、公钥密码 完整性--散列类算法 可用性--散列类、公钥密码 真实性--公…

项目雅景临居---模块2 标签管理

一、 [根据类型]查询标签列表 Autowiredprivate LabelInfoService service;Operation(summary "&#xff08;根据类型&#xff09;查询标签列表")GetMapping("list")public Result<List<LabelInfo>> labelList(RequestParam(required false)…

Linux学习笔记6 进程角度看内存泄露

一&#xff0c;从进程角度看堆区内存申请与释放问题 1&#xff0c;c语言中的内存泄漏 内存溢出&#xff1a;申请内存时&#xff0c;没用足够的内存可以使用。 内存泄露&#xff1a;严格来说&#xff0c;只有对象不会再被程序用到了&#xff0c;但是GC又不能回收它们的情况&…

ue项目更改项目缓存 防止跑ue项目c盘占用过多内存

原本项目缓存 在ue引擎所在的文件夹里找好baseEnging.ini 搜索install 找到第7个install 更改缓存路径到指定位置