Redisson分布式锁源码-可重入锁的八大机制-下(2)
可重入锁释放锁的场景
(1)客户端宕机导致锁释放
这个比较好理解一点,如果Redisson客户端刚加锁成功,此时后台肯定就有一个后台watchdog的定时任务每隔10s检查key,key如果存在就为它
自动续期30s,当watchdog定时任务存在的情况下,如果不是主动释放锁,那么key将会一直的被watchdog这个定时任务维持加锁。
如果客户端宕机了,此时watchdog定时任务当然也就是没了,既然没有了定时任务定时的为key续期,那么过完了30s之后,key自动就会被删除、
key对应的锁也就释放了。
客户端主动释放锁
当客户端主动释放锁时,就会调用unlock方法,如下图所示:
跟进去看下底层的调用逻辑:
和上锁逻辑一样,释放锁的逻辑也是通过lua脚本来完成的,通过方法后面的参数我们可以知道:
KEYS[1]=anyLock
ARGV[2]=30000
ARGV[3]=UUID:ThreadId
第一个if中,redis.call(‘exists’,KEYS[1])==0 ,如果发现key不存在,发布订阅机制就会发布一些东西,这些和redis相关的订阅发布机制可暂时忽略;key此时当然是存在的,if分支跳过;
第二个if中,redis.call(‘hexists’,KEYS[1],ARGV[3])==0 ,如果发现key对应hash数据结构中、当前线程不存在或没有当前线程的信息,直接就返回了;此时key对应的hash数据结构中当然有当前线程的信息啊,跳出if分支;
然后通过命令:
hincrby KEYS[1] ARGV[3] -1,即 hincrby anyLock UUID:ThreadId -1
这里表示将线程UUID:ThreadId线程对key加锁的重入次数减1,即:
anyLock: {
UUID:ThreadId 2
}
变为
anyLock: {
UUID:ThreadId 1
}
此时,如果当前线程对这个key的重入锁次数counter还大于0,就表示当前线程对这个key不止加过一次锁,此时当然就不能删掉key啊,就执行redis.call(‘pexpire’,KEYS[1],ARGV[2]), 重置下key的存活时间为30s;
如果当前线程对这个key的重入锁次数counter等于0了,表示当前线程只对这个key加过一次锁,现在刚好释放一次锁,线程对该key已经没有加锁记录了,当然就直接执行redis.call(‘del’,KEYS[1])删除了;
这样也比较合理,因为如果说一个线程对一个key重入好几次锁,你只调用了一次unlock方法,就把别人key给删除了,那要重入锁又有什么意义呢。
释放锁和加锁其实是呼应的,你既然加锁是在key的hash数据结构中添加当前信息的记录,那释放锁当然也就是加锁的逆向操作了,包括锁重入次数的递增、key的删除等。