redis学习(015 实战:黑马点评:分布式锁)

黑马程序员Redis入门到实战教程,深度透析redis底层原理+redis分布式锁+企业解决方案+黑马点评实战项目

总时长 42:48:00 共175P

此文章包含第56p-第p63的内容


分布式锁介绍

在这里插入图片描述

不使用分布式锁的情况
在这里插入图片描述
使用分布式锁的情况
在这里插入图片描述
在这里插入图片描述

分布式锁的满足条件

这里一些可以满足也可以不满足的功能,如: 是否满足可重用性、是公平锁还是非公平锁、是阻塞的还是非阻塞的等

在这里插入图片描述
在这里插入图片描述

基于redis的分布式锁

获取锁

在这里插入图片描述
只有第一个成功了
在这里插入图片描述

释放锁

在这里插入图片描述
在这里插入图片描述
如果服务挂了,这里无法释放锁,就会产生死锁
这时候我们需要添加过期时间来阻止此类情况发生

在这里插入图片描述
在这里插入图片描述
这里要确保setnx 锁 和加过期时间保持原子性,要不然杠添加了锁,还没加过期时间就宕机了就完了,
所以我们使用一个命令来执行

SET 锁 值(线程名) NX EX 10
在这里插入图片描述

在这里插入图片描述
10秒内无法添加
在这里插入图片描述

过期之后又可以添加了
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

编写代码

在这里插入图片描述

在这里插入图片描述

创建一个接口

在这里插入图片描述

定义一个类 实现这个接口

在这里插入图片描述

在这里插入图片描述
定义锁的名称 前缀 等信息
在这里插入图片描述
定义一个构造函数 让其传入这两个值
在这里插入图片描述

调用redisTemplate的setnx方法 即 setIfAbsent()方法
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

担心产生null 这里使用Boolean.TRUE.equals方法 只有true时返回true 其他时候是false
也可以使用hutool的方法实现

获取锁

在这里插入图片描述

释放锁

在这里插入图片描述

测试

原来的代码

在这里插入图片描述

修改后的代码

在这里插入图片描述
创建锁对象 使用的name我们是 业务:用户ID 这样锁的范围很小
在这里插入图片描述
因为这是自己写的工具类,需要实现复用,如果要注入就不能在别的地方用了,所以我们使用传入stringTedisTemolate的方式

也可以设置用户id+优惠卷id拼接

在这里插入图片描述
在这里插入图片描述

开始测试,测试多个jvm的情况

用postman发送两个请求
在这里插入图片描述

第一个是lock为true
在这里插入图片描述

第二个是lock为false
在这里插入图片描述

redis存到的数据
在这里插入图片描述

分布式锁误删问题

如果业务阻塞时间太久 超过了过期时间 直接释放锁了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这时候线程1把线程2的锁释放了 这时候线程2还没完成,线程3就进来了
在这里插入图片描述
在这里插入图片描述
这样就导致线程安全问题

应该在释放锁的时候判断一下是不是自己这个线程的锁 别把别人的锁给开了
在这里插入图片描述

在这里插入图片描述

改造代码

在这里插入图片描述
在这里插入图片描述
直接使用线程id的时候 集群情况下会出现冲突 这里我们加上uuid

ps:这里的uuid使用static final声明,这个uuid前缀一直不变,这样相当于这个uuid代表了本次启动后 这个jvm的一个唯一值
在这里插入图片描述

修改释放代码
在这里插入图片描述

开始测试

tomcat1 获取锁 然后阻塞住 然后手动删除模拟过期
在这里插入图片描述

在这里插入图片描述

让tomcat2执行,也获取到了锁
在这里插入图片描述
让tomcat1释放锁 ,可以看到两个id不一样
在这里插入图片描述

在这里插入图片描述

tomcat2释放锁
在这里插入图片描述
锁被删除
在这里插入图片描述
在这里插入图片描述

原子性

在一些极端条件下 还是存在问题
在这里插入图片描述
如果jvm垃圾回收的时间特别长 阻塞的时间超过了线程锁的过期时间

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

所以判断锁和释放锁的两个命令必须是原子性操作才可以
在这里插入图片描述

使用lua命令

在这里插入图片描述
可以使用lua脚本保持命令的原子性
在这里插入图片描述
可以进入网站学习lua脚本命令
在这里插入图片描述

在这里插入图片描述

不带参数的写法

后面的0代表本次没有参数进来 是个写死的脚本
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

带参数的写法

切记 lua的数组下标是从1开始的不是0
在这里插入图片描述
这里的1代表key的数量
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

业务流程

在这里插入图片描述
用vscode新建一个文件 然后 选择编程语言 这样就可以选择lua语言 这样有高亮提示(idea也有lua插件)
在这里插入图片描述

在这里插入图片描述

判断当前的锁id是否相同,相同就删除 的lua脚本
在这里插入图片描述

简化脚本
在这里插入图片描述

在这里插入图片描述

编写脚本java代码

在这里插入图片描述

我们创建一个lua脚本文件 (创文件.lua后缀,idea会提示安装插件)
在这里插入图片描述

这里注意,虽然返回值只是0和1,但是也要用Long类型接值。脚本执行不支持接受Integer类型

使用 ClassPathResource()方法找到classpath下的文件
在这里插入图片描述
我们的文件放在 resources文件夹下 就直接就是classpath

在这里插入图片描述
代码如下· 因为我们放到了static块下 因此这个类加载的时候就会去访问unlock.lua,这个脚本就被初始化了
在这里插入图片描述

Collections.singletonList单元素数组
在这里插入图片描述
这时已经将代码变成了一行 就不会出现原子性问题了
在这里插入图片描述

总结

在这里插入图片描述


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值