当前位置:网站首页>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。这时所有的线程就会继续循环,尝试下一次加锁。
边栏推荐
- 1、 Ffmpeg record audio as PCM file
- 2022-05-24 SpEL使用
- OJ 1507 删数问题
- Treasure plan TPC system development DAPP construction
- Icc2 (IV) routing and postroute optimization
- MFC uses the console to print program information
- Bug experience related to IAP jump of stm32
- 费马小定理
- 什么气传导蓝牙耳机好、配置比较高的气传导耳机推荐
- Dynamic programming -- simple question type of climbing stairs
猜你喜欢
随机推荐
OJ 1018 报数游戏
Leetcode 刷题日记 剑指 Offer II 053. 二叉搜索树中的中序后继
2022-07-17 达梦数据库安装
Icc2 (IV) routing and postroute optimization
【无标题】
scrapy 命令
【详解如何一步步实现三子棋】
OJ 1131 beautiful number
Execjs call
OpenGL quick configuration method
QT solves the problem of rebuilding UI files every time they are modified
二维数组实战:螺旋矩阵
C语言的编译和预处理
【动态规划--买卖股票的最佳时期系列2】
MFC uses the console to print program information
Battle plague Cup -- strange shape
气传导蓝牙耳机哪个好、气传导蓝牙耳机排行榜
用c语言实现三子棋小游戏
[c语言]--一步一步实现扫雷小游戏
气传导耳机哪个好、性价比最高的气传导耳机推荐









![[untitled]](/img/de/746832bfb3bb79b090215b135b8917.jpg)
