Redisson源码分析
- 一、加锁时使用lua表达式,执行添加key并设置过期时间
- 二、加锁成功之后给锁添加对应的事件
- 三、加锁完成,看门狗自动续命未处理完的线程
- 四、线程加锁不成功,加锁自旋+阻塞
- 五、释放锁之后触发事件,释放信号量
- 六、补充RedissonMultiLock(RedissonRedLock)
Redisson底层实现锁的逻辑图
Redisson分布式锁原理:
- 可重入:利用hash结构记录线程id和重入次数
- 可重试:利用信号量和PubSub功能实现等待、唤醒,获取锁失败的重试机制
- 超时续约:利用watchDog,每隔一段时间(releaseTime/3),重置超时时间
一、加锁时使用lua表达式,执行添加key并设置过期时间
完成加锁操作,并添加锁的超时时间(默认为30S),类比理解setnx命令
获取锁成功,返回null,获取锁失败,返回key的剩余时间
二、加锁成功之后给锁添加对应的事件
1、
加锁失败后,给锁注册对应的PUBSUB事件
2、
最终方法对调用到subscribe方法,在该方法内部又会添加对应的监听事件
3、
第五步,释放锁之后会触发该onMessage方法
当触发PUBSUB事件的时候,就会调用到这个onMessage方法。如果该PUBSUB事件是一个onlockMessage事件,则释放对应的信号量,即调用release方法
三、加锁完成,看门狗自动续命未处理完的线程
1、
tryLockInnerAsync方法是一个异步任务,当加锁成功会会进入下面的operationComplete方法
2、
在加锁成功,并且锁没有释放的基础上,使用lua脚本,对还没有释放的锁重新设置30S的后台看门狗续命时间。
续命成功之后会再次递归调用自己,完成下一次的续命逻辑。
四、线程加锁不成功,加锁自旋+阻塞
与第一步相同的起点,如果第二把锁加锁不成功,返回的是第一把锁的超时时间。在while循环中,使用tryAcquire方法尝试加锁,直到加锁成功后,才会break(加锁成功返回null)
底下有一个getEntry().tryAcquire方法,该方法利用Semaphore信号量的阻塞机制。如果没有获取到对应的信号量,就阻塞,从而避免cpu空转,直到信号量释放之后(调用release方法,即第二步的onMessage方法中),才能继续往下走。
五、释放锁之后触发事件,释放信号量
释放锁之后会添加一个释放锁的事件(unlockMessage)
该步骤就会触发第二步的onMessage方法,然后调用release方法释放对应的信号量。
释放完信号量,第三步中阻塞的线程就被唤醒,开启新一轮的while循环,再次尝试获取锁,如此往复。
此时让你再来梳理这个逻辑,应该就得心应手了
图片来源黑马,侵删
六、补充RedissonMultiLock(RedissonRedLock)
1、
RedissonMultiLock加锁时是使用迭代器,对所有需要加锁的对象依次进行加锁操作
2、
如果出现其中一个加锁不成功,那么就迭代上一个,让上一个再进行加锁,这就会出现上一个加锁不成功,然后递归向上到第一个锁,从头开始执行加锁操作
3、
这里表示我们会重新设置所有锁对象的过期时间