基于redis的分布式锁

一、redis分布式锁基本信息

1.详细讲解:

Redis 分布式锁是一种用于控制分布式系统中多个进程对共享资源的并发访问的机制。通过 Redis 的原子操作和过期时间功能,可以实现一个简单而有效的分布式锁。接下来,我们将详细介绍其工作原理、基本操作步骤以及代码实现,并提供详细的注释。

2.基本原理

2.1获取锁:

使用 SET 命令尝试设置一个键,并使用 NX 参数(如果键不存在则设置)和 PX 参数(设置键的过期时间)。
如果 SET 操作返回成功(即 OK),则表示获取锁成功。

2.2释放锁:

释放锁时需要确保只有持有锁的客户端才能释放锁,因此需要在释放锁时检查锁的值是否是当前客户端的标识。
使用 Lua 脚本来保证原子性:检查锁的值并删除锁。

二、代码示例

1.redis加锁以及解锁的工具类

1.1注意事项:

1.1.1 由于RedisTemplate交由spring管理,所以我们工具类也需要注册给spring,之后通过注入方式使用工具类,这样redisTemplate就不会是null;
1.1.2 用脚本的方式执行redisTemplate.execute会出现异常java.lang.UnsupportedOperationException: io.lettuce.core.output.ValueOutput does not support set(long),好像是返回值无法转换,由于redis版本不匹配,我这边redis版本是5.0.14.1;(暂未解决脚本执行后续会修改);

@Slf4j
@Component
public class LockUtil {@Resourceprivate RedisTemplate<String, Object> redisTemplate;//定义变量private final Object RELEASE_SUCCESS = 1;private final Object RELEASE_ERROR = 0;/*** 以阻塞方式的获取锁* @param key         key* @param value       value* @param lockTimeout 锁超时时间* @param getTimeout  获取锁超时时间* @return*/public boolean lockBlock(String key, String value, long lockTimeout, long getTimeout, TimeUnit timeUnit) {long start = System.currentTimeMillis();//循环执行是否能加锁成功判断while (true) {//检测是否超时if (System.currentTimeMillis() - start > getTimeout) {log.error(Thread.currentThread().getName() + "get lock timeout");return false;}//执行set命令 ,如果返回 true,表示获取锁成功;如果返回 false,表示获取锁失败。Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, value, lockTimeout, timeUnit);//是否成功获取锁if (absent != null && absent) {return true;} else {log.info(Thread.currentThread().getName() + "get lock fail:{},{}", key, value);}}}/*** 解锁:由于我这边无法执行redis脚本,一直返回异常,下面redis操作并非是原子性操作* @param key 加锁key* @param value 锁value* @return 返回是否解锁成功*/public boolean unlock(String key, String value) {Object result = 0;String o = (String) redisTemplate.opsForValue().get(key);if (o != null && o.equals(value)) {Boolean delete = redisTemplate.delete(key);if (delete){result = 1;}}if (RELEASE_SUCCESS.equals(result)) {return true;}log.error(Thread.currentThread().getName() + "unlock error");return false;}
}

2.调用实现

采用多线程方式模拟调用

@Slf4j
@RestController
public class TestController {@Resourceprivate LockUtil lockUtil = new LockUtil();@GetMapping(value = "/redisLock")public void testRedis(@RequestParam(value = "key") String key) throws ExecutionException, InterruptedException {ExecutorService threadPoolTaskExecutor = Executors.newFixedThreadPool(10);CompletableFuture<Void> completableFuture1 = this.getCompletableFuture(threadPoolTaskExecutor, key);CompletableFuture<Void> completableFuture2 = this.getCompletableFuture(threadPoolTaskExecutor, key);CompletableFuture.allOf(completableFuture1, completableFuture2).get();threadPoolTaskExecutor.shutdown();}private CompletableFuture<Void> getCompletableFuture( ExecutorService threadPoolTaskExecutor,String key){CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {String value = UUID.randomUUID().toString();try {if (lockUtil.lockBlock(key, value, 3L, 10L, TimeUnit.SECONDS)) {log.info(Thread.currentThread().getName() + "获取锁成功,value is {}", value);Thread.sleep(2000);} else {log.info(Thread.currentThread().getName() + "获取锁失败,value is {}", value);}} catch (InterruptedException e) {log.info(Thread.currentThread().getName() +"获取锁异常,value is {}", value);} finally {if (lockUtil.unlock(key, value)) {log.info(Thread.currentThread().getName() + "释放锁,value is {}", value);}}}, threadPoolTaskExecutor);return  completableFuture;}
}

3.测试结果显示

运行结果

4.上面方法解决的问题

4.1通过对于redis key添加过期防止锁无法释放造成死锁;
4.2通过加锁时间限制防止加锁失败一直加锁,造成死锁;
4.3通过value比对保证解的锁是自己持有的;

5.上面方法存在的问题

5.1代码执行超过redis存放时间:

redis释放之后,别人可以获取锁这样锁就相当于失效,解决方案:看门狗机制,我们redisson已经解决上面问题了

5.2代码实现

1.redisson配置

@Configuration
@Slf4j
public class RedissonConfig {@Beanpublic RedissonClient createRedissonClient() {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");//.setPassword(""); // 如果没有密码,可以省略这一行return Redisson.create(config);}
}

2.代码调用实现

@RestController
public class TestController {@Resourceprivate RedissonClient redissonClient;@GetMapping(value = "/redisLock")public void testRedis(@RequestParam(value = "key") String key) throws ExecutionException, InterruptedException {ExecutorService threadPoolTaskExecutor = Executors.newFixedThreadPool(10);CompletableFuture<Void> completableFuture1 = this.getCompletableFuture(threadPoolTaskExecutor, key);CompletableFuture<Void> completableFuture2 = this.getCompletableFuture(threadPoolTaskExecutor, key);CompletableFuture.allOf(completableFuture1, completableFuture2).get();threadPoolTaskExecutor.shutdown();}private CompletableFuture<Void> getCompletableFuture(ExecutorService threadPoolTaskExecutor, String key) {RLock lock = redissonClient.getLock(key);CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {try {// 尝试获取锁,等待时间10秒,上锁时间5秒if (lock.tryLock(10, 5, TimeUnit.SECONDS)) {log.info(Thread.currentThread().getName() + "获取锁成功");Thread.sleep(2000);} else {log.info(Thread.currentThread().getName() + "获取锁失败");}} catch (InterruptedException e) {log.info(Thread.currentThread().getName() + "获取锁异常");} finally {lock.unlock();log.info(Thread.currentThread().getName() + "释放锁成功");}}, threadPoolTaskExecutor);return completableFuture;}
}

3.运行结果

在这里插入图片描述
不足之处,望海涵,希望大佬指点;

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

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

相关文章

引入tinyMCE富文本框在vue3中的使用

实现效果&#xff1a; 官网地址&#xff1a;TinyMCE 7 Documentation | TinyMCE Documentation 1.下载依赖&#xff08;我使用的版本是5.0 目前最新版本到7了&#xff09; pnpm/npm install tinymce5.0.0 -S pnpm/npm install tinymce/tinymce-vue -S 2.在public文件夹下…

模拟电子之电子管

如果要实现一个放大器的功能&#xff0c;需要一个固定的放大倍数&#xff08;Gain&#xff09;&#xff0c;这也就是说输出信号应该是跟随输入信号变化而变化&#xff0c;换句话说输出信号应该要受到输入信号的控制。 在电子学中使用的最多的两个物理量就是电压和电流&#xf…

Java的核心类库

引言 在Java编程中&#xff0c;熟练掌握常用类与对象操作是开发的基础。Java的核心类库提供了丰富的功能&#xff0c;可以帮助开发者高效地处理各种编程任务。本文将详细介绍Java字符串操作、集合框架、日期与时间处理等内容&#xff0c;并通过图表和表格进行总结与示范。 字符…

java1.8运行arthas-boot.jar运行报错解决

报错内容 输入java -jar arthas-boot.jar&#xff0c;后报错。 [INFO] JAVA_HOME: D:\developing\jdk\jre1.8 [INFO] arthas-boot version: 3.7.2 [INFO] Can not find java process. Try to run jps command lists the instrumented Java HotSpot VMs on the target system.…

IPv6 ND 协议功能概述

ND 协议功能概述 ND&#xff08;Neighbor Discovery&#xff0c;邻居发现&#xff09;协议是 IPv6 的一个关键协议&#xff0c;它综合了 IPv4 中的 ARP&#xff0c;ICMP 路由发现和 ICMP 重定向等协议&#xff0c;并对它们做了改进。 作为 IPv6 的基础性协议&#xff0c;ND 协…

乐鑫 ESP32-C6 现身 Apple WWDC 2024 官方 Swift Demo

北京时间 6 月 11 日凌晨 1 点&#xff0c;苹果 2024 年全球开发者大会 (WWDC) 开幕。WWDC 是苹果公司每年举办一次的会议&#xff0c;旨在和全球开发者交流最新的技术和工具。大会通常分为主题演讲、技术讲座、实验室和论坛交流等板块。 苹果公司 WWDC 2024 开发者大会 在今年…

Lua解释器裁剪

本文目录 1、引言2、文件功能3、选择需要初始化的库4、结论 文章对应视频教程&#xff1a; 已更新。见下方 点击图片或链接访问我的B站主页~~~ Lua解释器裁剪&#xff0c;很简单~ 1、引言 在嵌入式中使用lua解释器&#xff0c;很多时候会面临资源紧张的情况。 同时&#xff0c…

Qt:QDialogButtonBox的使用

QDialogButtonBox是Qt自带的按钮箱&#xff0c;通过枚举QDialogButtonBox::ButtonRole可以添加Qt定义按钮&#xff0c;或者通过方法QDialogButtonBox::addButton添加自定义的按钮。 // 自定义按钮。 button_box_ new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonB…

Visual Studio编译fatal error C1001: 编译器中发生内部错误

项目本来是能正常编译的&#xff0c;但是突然出现“fatal error C1001: 编译器中发生内部错误。” 2> (编译器文件“f:\dd\vctools\compiler\utc\src\p2\main.c”&#xff0c;第 255 行) 2> 要解决此问题&#xff0c;请尝试简化或更改上面所列位置附近的程序。 2> …

网络编程2----UDP简单客户端服务器的实现

首先我们要知道传输层提供的协议主要有两种&#xff0c;TCP协议和UDP协议&#xff0c;先来介绍一下它们的区别&#xff1a; 1、TCP是面向连接的&#xff0c;UDP是无连接的。 连接的本质是双方分别保存了对方的关键信息&#xff0c;而面向连接并不意味着数据一定能正常传输到对…

高考志愿填报,选错了专业怎么办?

高考充满惊喜也充满遗憾&#xff0c;惊喜的是分数出来的时候自己可能会满意&#xff0c;可能会选到一个好的专业&#xff0c;遗憾的是自己可能分数不理想&#xff0c;想读的专业因分数不达标作罢。在进行专业选择时&#xff0c;也有可能因为父母的建议&#xff0c;放弃了自己最…

集合java

1.集合 ArrayList 集合和数组的优势对比&#xff1a; 长度可变 添加数据的时候不需要考虑索引&#xff0c;默认将数据添加到末尾 package com.itheima;import java.util.ArrayList;/*public boolean add(要添加的元素) | 将指定的元素追加到此集合的末尾 | | p…

gpt、llama大模型模型结构细节探索

参考&#xff1a; https://github.com/naklecha/llama3-from-scratch&#xff08;一定要看看&#xff09; https://github.com/karpathy/build-nanogpt/blob/master/play.ipynb 视频&#xff1a; https://www.youtube.com/watch?vl8pRSuU81PU https://tiktokenizer.vercel…

编辑并保存hosts文件

1.以管理员权限打开cmd 2.执行命令 notepad C:\Windows\System32\drivers\etc\hosts 回车后会通过记事本打开hosts文件&#xff0c;然后就可以编辑并保存了。

Django中使用下拉列表过滤HTML表格数据

在Django中&#xff0c;你可以使用下拉列表&#xff08;即选择框&#xff09;来过滤HTML表格中的数据。这通常涉及两个主要步骤&#xff1a;创建过滤表单和处理过滤逻辑。 创建过滤表单 首先&#xff0c;你需要创建一个表单&#xff0c;用于接收用户选择的过滤条件。这个表单可…

【C++高阶】C++继承学习手册:全面解析继承的各个方面

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;模板进阶 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 继承 &#x1f4d6;1. 继承的概念及定义…

使用 Django 创建 App

文章目录 步骤 1&#xff1a;创建 Django 项目步骤 2&#xff1a;创建 App步骤 3&#xff1a;配置 App步骤 4&#xff1a;编写代码步骤 5&#xff1a;运行服务器 在 Django 中&#xff0c;App 是组织代码的基本单元&#xff0c;它可以包含模型、视图、模板等组件&#xff0c;帮…

LabVIEW RT在非NI硬件上的应用与分析

LabVIEW RT&#xff08;实时操作系统&#xff09;可运行在非NI&#xff08;National Instruments&#xff09;硬件上&#xff0c;如研华工控机&#xff0c;但需要满足特定硬件要求。本文从硬件要求、开发和运行差异、可靠性、稳定性、优势和成本等多角度详细分析在非NI硬件上运…

可视化大屏:颜值低了,就要多看多学习,切忌讳疾忌医。

Hi&#xff0c;这是第15期了&#xff0c;再发一波three.js的效果。 可视化大屏与Three.js结合可以创建出令人印象深刻的交互式3D可视化效果&#xff0c;为用户呈现更加生动和交互式的数据展示。

Wells Fargo 借助 MongoDB 推出下一代银行卡支付

MongoDB客户案例导读 MongoDB以其灵活的数据模型、高性能的实时分析能力和可扩展的分布式架构&#xff0c;帮助Wells Fargo富国银行显著提升了数据处理效率和业务响应速度&#xff0c;为其大型机现代化和数字化转型提供了强有力的技术支撑。 金融服务市场正在经历一场变革&am…