当前位置: 首页>后端>正文

Redisson分布式锁

一:为什么需要分布式锁

一般说到锁,我们会想到java的synchronized与Reentrantlock。Java 的 synchronized或者Reentrantlock 锁只能保证在同一个 JVM 进程内的多线程并发安全性。但是在多线程并发的情况下,并不能保证线程安全。因为现在的业务大多都是集群部署,也就是多个 JVM 实例,所以 Java 内置的锁无法保证集群环境的多线程安全性。所以如果业务是集群部署,那就需要分布式锁来保证整合集群的线程安全

二? 分布式锁的特性

1:高性能

2:互斥性

3:可重入

4:防止死锁

三???分布式锁常用的几种实现方式

1:数据库(Mysql)

2:? redis

3:??ZooKeeper

四??Redisson-lock()与unLock()

首先Redis作为分布式锁有几个很重要的关键点,就是要满足原子性,正确的释放锁,过期时间以及锁续期。以下我们分析一下

1:引入依赖

<dependency>

? ? ? ? <groupId>org.redisson</groupId>

? ? ? ? <artifactId>redisson</artifactId>

? ? ? ? <version>3.11.1</version>

? ? </dependency>

2:查看一下lock方法

我们通过tryAcquire方法一层层进去看到最终是通过lua脚本来保证多个命令的原子性。

核心流程如下:

1: 请求进来判断当前执行的方法有没有上锁,如果没有上锁则进行上锁,上锁成功后返回nil。

2: 如果当前执行的方法已经有锁了,那就看持有锁的线程是不是自己,是自己就代表重入,直接给锁状态hincrby + 1,然后返回nil代表成功。

3: 如果当前执行的方法已经有锁但持有锁的线程不是当前请求的线程,那则代表竞争抢锁了,需要互斥,就pttl命令返回当前锁的剩余时间,然后外层进行等待重试。

Tips:ARGV[2]是一个由uuid:threadId组成的一个唯一的数值,在集群部署的情况下,threadId在不同机器上是可以重复的,所以用到uuid.

Redisson分布式锁,第1张

释放锁主要是unlock(),主要逻辑在unlockInnerAsync方法,主参数是根据线程id.因为有锁重入的情况,所以需要判断是否有锁重入,如果有锁重入那就先把锁重入次数减1,当没有锁重入的时候直接删除锁即可,核心源码如下:

如果当前线程没有持有锁,我们看源码可以看到返回 nil,因为无锁就不需要释放所以是直接返回。

但是如果当前线程持有锁,则执行hincrby, myLock, uuid:threadId, -1,这里为啥减1就是因为会有锁重入的情况。

减完1后需要判断值是否还大于0,如果大于0则代表是有锁重入的,还没释放完就不能删除掉,需要重新给锁续期。

如果减完1后值小于0,那就代表释放完了,没有锁重入的情况了,所以就可以直接删除掉,结果返回1就代表解锁成功。

Redisson分布式锁,第2张

五:锁续期

背景

如果有一个线程A持有锁,但是业务还没有执行结束,这个时候锁过期了,如果在这时其他线程可以抢锁,那么就会造成线程A与别的线程同时执行了上锁的业务逻辑,这样会造成了线程不安全。

解决办法:锁续期

锁续期就是专门用来解上述问题的,Redisson有个watchDog机制,我们现在看一下源码分析一下watchDog是如何解决的以及他心原理是什么。

watchDog是怎么生效的?

我们从加锁逻辑一层层找看哪里做了锁续期的操作,我们看到了tryLockInnerAsync方法里有个watchDog字段信息。

RFuture ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);

我们可以发现每次加锁都会判断是否开启watchDog,只有leaseTime == -1的时候才会走开启看门狗的逻辑。什么时候等于-1呢,leaseTime是什么?

Redisson分布式锁,第3张

根据LongtryAcquire方法向上层代码逻辑看一下,发现leaseTime是lock()入口带来的。用户加锁是可以定义时间的,这个时间就是锁过期时间。也就是传入锁过期时间就会开启看门狗。

private LongtryAcquire(long leaseTime, TimeUnit unit, long threadId) {

return get(tryAcquireAsync(leaseTime, unit, threadId));

}

Redisson分布式锁,第4张

scheduleExpirationRenewal方法就是锁需求的实现方法

Redisson分布式锁,第5张

1:锁续期的核心方法

2:续期成功调用自己

3:续期的起始时间是超过过期时间的三分之一,比如你设置过期时间30秒,过了10秒你还没执行完就会续期。

Redisson分布式锁,第6张
Redisson分布式锁,第7张

https://www.xamrdz.com/backend/3u91924823.html

相关文章: