日常开发中,基于 redis 天然支持分布式锁,大家在线上分布式项目中都使用过 Redis 锁 。本文主要针对日常开发中加锁过程中某些异常场景进行讲解与分析 。本文讲解示例代码都在 https://Github.com/wayn111/newbee-mall-pro 项目 test
目录下 RedisLockTest
类中 。
版本声明:
-
Spring Boot
版本 3.0.2 - 演示项目地址:https://github.com/wayn111/newbee-mall-pro
- github地址:http://github.com/wayn111 欢迎大家关注,点个star
那怎么处理这个异常嘞?大部分人可能都会回答添加一个定时任务 , 在定时任务内检测锁快过期时 , 进行续期操作 。OK,这么做好像是可以解决这个异常,那么博主在这里给出自己的见解 。
1.1 先说一个暴论:如果料想到有这类异常产生,为什么不在加锁时,就把加锁过期时间设置大一点不管所续期还是增大加锁时长,都会导致一个问题,其他线程会迟迟获取不到锁,一直被阻塞 。那结果都一样 , 为什么不直接增大加锁时间?
?想法是好的,但是实际上,加锁时间的设置是我们主观臆断的 , 我们无法保证这个加锁代码的执行时间一定在我们的锁过期时间内 。作为一个严谨的程序员,我们需要对我们的代码有客观认知 , 任务执行可能几千上亿万次都是正常,但就是那么一次它执行超时了 , 可能由于外部依赖、当前运行环境的异常导致 。1.2 直接不设置过期时间,任务不执行完 , 不释放锁如果在加锁时就不设置过期时间的话,理论上好像是可以解决这个问题 , 任务不执行完,锁就不会释放 。但是作为程序员 , 总觉得哪里怪怪的,任务不执行完,锁就不会释放!
?
?仔细想想,我们一般在 try 中进行加锁 在 finally 进行锁释放 , 这个好像也没毛病哦 。但是实际针对一些极端异常场景下,如果任务执行过程中,服务器宕机、程序突然被杀掉、网络断连等都可能造成这个锁释放不了,另一个任务就一直获取不到锁 。这个方案程序正常的情况下 , 可以满足我们的要求,但是一旦发生异常将导致锁无法释放的后果,也就是说只要我们解决这个锁在异常场景下无法释放的问题 , 这个方案还是OK的 。博主这里直接给出方案:
?
在不设置过期时间的加锁操作成功时,给一个默认过期时间比如三十秒 , 同时启动一个定时任务,给我们的锁进行自动续期,每隔
默认过期时间 / 3
秒后执行一次续期操作 , 发生锁剩余时长小于 默认过期时间 / 2
就重新赋值过期时长为三十秒 。这样的话,可以保证锁必须由任务执行完才能释放,当程序异常发生时,仍然能保证锁会在三十秒内释放 。1.3 设置过期时间 , 任务不执行完,不释放锁这个方案本质上与方案二的解决方案相同 , 还是启动定时任务进行续期操作,流程这里不做多余讲述 。需要注意的就是加锁指定过期时间会比较符合我们的客观认知 。实际上他的底层逻辑跟方案二相同,无非就是定时任务执行间隔,锁剩余时长续期判断要根据过期时间来计算 。
「综合来看:方案三会最合适,符合我们的客观认知,跟我们之前对 Redis 的使用逻辑较为相近 。」
二、线程B加锁执行中未释放锁,线程A释放了线程B的锁
?说实话我仔细思考了一下这个异常场景 , 发现这个异常是个伪命题,如果线程 B 正在执行时,线程 A 怎么能获取到线程B的锁!线程 A 获取不到线程 B 的锁,谈何来去释放线程 B 的锁!如果线程 A 能获取到线程 B 的锁那么这个分布式锁的代码一开始就已经错了 。这里回到这个异常场景本身 , 我们可以给每个线程设置请求ID,加锁成功将请求ID设置为加锁 key 的对应 value,线程释放锁时需要判断当前线程的请求ID与 加锁 key 的对应 value 是否相同,相同则可以释放锁,不相同则不允许释放 。
?
推荐阅读
- Redis 也支持全文搜索?这也太强了
- 聊聊分布式数据库TDSQL的技术架构
- pr怎么锁定图层,ps要怎么才可以锁定图层
- 华为手机触屏解锁怎么设置,华为解锁直接进入桌面怎么设置
- 刘德华11岁女儿时隔5年露面!眉头深锁送别爷爷,长得与妈妈极像
- 解锁 C++ 并发编程的钥匙:探索 Atomic 变量
- MySQL:逃不掉的锁事,间隙锁
- 宋祖英终于换对新发型,56岁嫩回36岁,锁骨短发配粉衣少女感十足
- 栖桐国际:面霜可以锁住皮肤表面,形成一个保护层,防止水分流失
- TVB女星自曝被赶出家门!密码锁被改衣服被扔掉,新婚4个月