当前位置:网站首页>redis实现分布式锁思路及redission分布式锁主流程分析
redis实现分布式锁思路及redission分布式锁主流程分析
2022-07-28 05:27:00 【yfyh2021】
1.redis实现分布式锁思路
首先,我们来看一段问题比较明显的代码。
/**
* 存在并发问题
* @param productId
* @return
*/
private String reduceStock1(String productId) {
Integer stock = Integer.parseInt(redisTemplate.opsForValue().get(productId));
if(stock>0){
int realStock = stock -1;
redisTemplate.opsForValue().set(productId,realStock+"");
System.out.println("扣减成功,剩余库存"+realStock);
}else {
System.out.println("扣减失败,库存不足");
}
return "";
}对于临界资源库存,我们明显有着并发问题,对于集群来说单体锁synchronized或者reentrantLock均不合适。
目前对于分布式锁来说有两种比较流行的解决办法,一种是利用redis,另一种是用zk,我们使用redis来看一下怎么实现。
- version 1

我们利用setnx的特性进行加锁(第一个红框),此时还非常粗糙,如果有用户获取锁成功但在释放锁之前(第二个红框)出现问题,则会产生死锁。
- version 2

用finally来保证代码即使有什么问题能够最终释放锁。但是如果服务器宕机了或者重启了怎么办,这时候就需要设置超时时间来保证锁不会被挂掉的线程一直占用。
- version 3

此时我们设置10s的锁时间,这个时候如果业务逻辑超过10s是不是其他的线程就有可能拿到锁并删除我们的锁。此时我们要保证谁加的锁谁才能解锁。
- version 4

这个时候就相对较为完整了。但是还有一个致命的问题,就是锁续命的问题,来保证我们在10s后能够继续生效,使finally里能够获取锁。这就需要再来启动一个线程用expire来给锁续命,并且保证定时执行。
2.redisson分布式锁实现
首先,我们来看一下redisson上锁的实现部分。

在红框中就是上锁的redis命令,这个部分是用lua脚本完成的,用来保证操作的原子性。
KEYS[1] --> Collections.<Object>singletonList(getName())ARGV[1] --> internalLockLeaseTimeARGV[2] --> getLockName(threadId)lua脚本的参数对应如上。
我们来看第一部分if,对应逻辑如下(下面的if为锁重入部分暂不讨论)。
- 锁对应的key是否存在。
- 如不存在则设置hash值。第一个key为我们传入的锁名,第二个key为UUID:线程id
- 设置有效时间,因为我们没有传入所以是默认的30*1000,也就是30s。
- 返回空
可见我们的redis锁设计思路是没有问题的,保存了锁名和uuid构成的唯一标识,设置了超时时间。我们接着看,看redisson是怎么实现锁续命的。

红框上一行代码就是我们上锁的代码,我们得到的是个返回值为空的future,来看看红框中的代码。可见核心就一行,就if里面的内容,通过命名我们也可以看出来就是周期重置有效时间。我们点进去看看。

我们先来看看大红框中的lua脚本。先判断锁是否存在,若在将我们的有效时间重置为30s。
然后进行了递归调用,并将调用时间设置为延迟三分之有效时间也就是10s,这样就巧妙的完成了我们的锁续命。
这种递归调用来实现定时任务是很多中间件采用的方法,大神们的写法值得我们学习。
那么,对于无法上锁的线程,redisson又是怎么处理的呢? 
在上锁的lua脚本中,对于无法获取锁的线程(红框中)则返回锁的剩余时间,我们在一层层网上看

第一个红框中为redis的channel,类似于mq,是用来通知其他线程锁释放的,后面的大红框为自旋。我们先来看一下后面的部分。
- 先是再次尝试加锁,返回锁剩余时间。
看到 semaphore进行tryAcquire,那么我们来找一下到底在哪里release的,大概率是在unlock的方法中实现的。
在unlock解锁方法中,我们看到前后的两个方框中都是发布了一个unlockMessage(为0L),返回了一个1也就是ture。我们来找一下onMessage方法

果然在unlock中进行了release。这时所有的线程就会继续循环,尝试下一次加锁。
边栏推荐
- Leetcode 刷题日记 剑指 Offer II 048. 序列化与反序列化二叉树
- 浮点型数据在内存中如何存储
- 什么气传导蓝牙耳机好、配置比较高的气传导耳机推荐
- Treasure plan TPC system development DAPP construction
- C语言的文件操作
- OJ 1253 点菜问题
- 2022-07-17 Damon database installation
- Get the current directory in QT
- OpenGL development environment configuration [vs2017] + frequently asked questions
- Execjs call
猜你喜欢

中国剩余定理 个人理解

开放式耳机有哪些、四款音质超好的气传导耳机推荐

Several methods of QT setting loading interface

2022-06-07 VI. log implementation

What's a good gift for your girlfriend on the Chinese Valentine's day in 2022? Practical and beautiful gift recommendation

Valgrind tool

What is hash? (development of Quantitative Trading Robot System)

水瓶效果制作
![[basic knowledge of binary tree]](/img/4f/9fc27a30b958af26537c299e150ee9.png)
[basic knowledge of binary tree]

【动态规划--买卖股票的最佳时期系列】
随机推荐
OJ 1131 美丽数
New Selenium
Execjs call
[PTA----输出全排列]
[basic knowledge of binary tree]
AQS之countDownLatch源码分析
[dynamic planning -- the best period for buying and selling stocks Series 2]
Icc2 use report_ Placement check floorplan
图形管线基础(二)
网络通信及TCP/IP协议
Leetcode brush question diary sword finger offer II 053. Medium order successor in binary search tree
Treasure plan TPC system development DAPP construction
What is the best and most cost-effective open headset recommended
【C笔记】数据类型及存储
图形管线基础(番外篇)
[PTA--利用队列解决猴子选大王问题】
OJ 1129 fraction matrix
OJ 1242 freshman's debut
【无标题】
Dynamic programming -- simple question type of climbing stairs
