Redisson源码解读—可重入、锁续命详解

本文详细解读了Redisson库中的锁重入、获取策略(包括看门狗机制和消息订阅)、自动续命以及释放锁的过程,强调了释放锁时的注意事项和watchdog机制的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码解读

1. Redisson的锁重入源码解读

首先,进入RedissonLock#tryLock(long waitTime, long leaseTime, TimeUnit unit)方法
在这里插入图片描述

  • waitTime : 获取锁的最大等待时长,第一次获取锁失败不会立即返回,而是在等待时间内不断的尝试,如果这个时间结束了都还没获取成功,才返回false;
  • leaseTime : 锁自动失效释放的时间,设置了该值后,Redisson的看门狗续命机制失效;
  • unit : 时间单位;

查看尝试获取锁的方法tryAcquire(),发现内部调用的是tryAcquireAsync()
在这里插入图片描述
查看tryAcquireAsync()内部方法
在这里插入图片描述

  • 判断是否设置了锁自动失效释放的时间(过期时间),设置与否走的方法都是tryLockInnerAsync(),只不过没有设置就赋默认internalLockLeaseTime值,这时Redisson会借助看门狗机制给锁自动续命(后面会讲);

查看tryLockInnerAsync()内部方法
在这里插入图片描述

  • lua脚本部分执行成功返回的是nil (类似于我们Java中的null),执行失败了反而返回一个结果: redis.call(‘pttl’, KEYS[1]) 也就是锁的剩余的有效期;
  • 执行了pttl命令,KEYS[1]是锁的名称,pttlttl效果是类似的,都是获取key的剩余有效期,只不过ttl返回的是秒为单位,pttl返回的是毫秒为单位;

现在已经拿到了锁的有效期, 我们现在往回倒一步
在这里插入图片描述

  • 红框内容涉及锁续命,不着急这里先跳过。
  • RFuture返回以后, 这里就有回到了这里get(tryAcquireAsync((wait, leaseTime, threadId))

get()方法就是获取阻塞等待RFuther结果, 等待得到的剩余有效期
在这里插入图片描述
这时就回到了这里
在这里插入图片描述

  • 这里的subscribe就是订阅释放锁的lua脚本中的publish
  • 如果等待结束还没有收到通知就取消订阅,并返回获取锁失败;

在这里插入图片描述

  • ttl<time(等待时间),代表在等待之间锁就已经释放了;
  • ttl>time(等待时间),如果等了time的时间,经过time的时间,锁还没有被释放,也就没必要等了;

在这里插入图片描述

  • 如果时间还很充足,就继续while(true)执行上面的代码,不停的尝试等待,不断的进行这样的循环;
  • 这里设计的巧妙之处就在于利用了消息订阅,信号量的机制,它不是无休止的这种盲等机制,也避免了不断的重试,而是检测到锁被释放才去尝试重新获取,这对CPU十分的友好;

2. WatchDog续约(续命)源码解读

Redisson锁重试的问题是解决了,但是总会发生一些问题,如果我们的业务阻塞超时了ttl到期了,别的线程看见我们的ttl到期了,他重试他就会拿到本该属于我们的锁,这时候就有安全问题了,所以该怎么解决?

我们必须确保锁是业务执行完释放的, 而不是因为阻塞而释放的。

在这里插入图片描述

  • 当我们没有设置leaseTime的时候, 也就是leaseTime=-1的时候过期时间为默认internalLockLeaseTime
  • 查看如下代码可知internalLockLeaseTime调用getLockWatchdogTimeout()赋值默认时间是30s;

在这里插入图片描述

在这里插入图片描述

  • ttlRemainingFuture的异步尝试获取锁完成以后,先判断执行过程中是否有异常,如果有异常就直接返回了结束执行;
  • 如果没有发生异常,则判断ttlRemaining(剩余有效期)是否为空,为空的话就代表获取锁成功,执行锁到期续约的核心方法scheduleExpectationRenew

进入scheduleExpectationRenew方法中查看

在这里插入图片描述

EXPIRATION_RENEWAL_MAP中的key我们进去看一下
在这里插入图片描述

  • 清楚的发现entryNameidname两部分组成;
  • id就是当前的这个连接的idname就是当前锁的名称;

进入更新有效期的方法renewExpectation

在这里插入图片描述

  • 这个方法主要开启一段定时任务,不断的去更新有效期,定时任务的的时间就是 看门狗时间/3,也就是10s后刷新有效期;

在这里插入图片描述
进入该刷新锁有效期方法
在这里插入图片描述

  • 这段lua脚本重置有效期,满血复活;

在这里插入图片描述

3. 释放锁

进入释放锁方法unlockAsync(long threadId)
在这里插入图片描述
进入释放锁的方法unlockInnerAsync(threadId)
在这里插入图片描述
进入 取消续命定时任务的方法cancelExpirationRenewal(threadId)
在这里插入图片描述

  • 先从map中取出任务,先移除任务的线程Id,再取消这个任务,最后再移除entry;
  • 到这里看门狗的流程就已经结束了;

4. 结论

watchdog在当前节点存活时每10s给分布式锁的key续期30s;
watchdog机制启动,且代码中没有释放锁操作时,watchdog会不断的给锁续期;
如果程序释放锁操作时因为异常没有被执行,那么锁无法被释放,所以释放锁操作一定要放到finally {}中;
要使watchLog机制生效,lock时不要设置过期时间;
watchlog的延时时间可以由lockWatchdogTimeout指定默认延时时间,但是不要设置太小;
watchdog会每lockWatchdogTimeout/3时间去延时;
watchdog通过类似netty的Future功能来实现异步延时;
watchdog最终还是通过lua脚本来进行延时。

5. 整体执行流程

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值