先留意一下属性internalLockLeaseTime,它在tryLockInnerAsync()方法内被重新赋值,在leaseTime == -1L的前提下,它被赋值为lockWatchdogTimeout,这个细节很重要,决定了后面续期方法(看门口)的调度频率。另外,leaseTime != -1L不会进行续期,也就是不会启动看门狗机制。
接着需要仔细分析一下tryLockInnerAsync()中执行的LUA脚本,笔者把它提取出来通过注释进行描述:
-- KEYS[1] == getName() --> $KEY --> resource:x -- ARGV[1] == internalLockLeaseTime --> 30000 -- ARGV[2] == getLockName(threadId) --> 559cc9df-bad8-4f6c-86a4-ffa51b7f1c36:1 -- 第一段代码是判断锁定的资源KEY不存在的时候进行相应值的设置,代表资源没有被锁定,首次获取锁成功 if (redis.call('exists', KEYS[1]) == 0) then -- 这里是设置调用次数,可以理解为延长KEY过期时间的调用次数 redis.call('hset', KEYS[1], ARGV[2], 1); -- 设置KEY的过期时间 redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; -- 第二段代码是判断HASH的field是否存在,如果存在说明是同一个线程重入的情况,这个时候需要延长KEY的TTL,并且HASH的field对应的value加1,记录延长ttl的次数 if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then -- 这里是增加调用次数,可以理解为增加延长KEY过期时间的调用次数 redis.call('hincrby', KEYS[1], ARGV[2], 1); -- 延长KEY的过期时间 redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; -- 第三段代码是兜底的,走到这里说明当前线程获取锁失败,锁已经被其他(进程中的)线程占有,返回当前KEY被占用资源的ttl,用来确定需要休眠的最大时间 return redis.call('pttl', KEYS[1]);这里画一个图演示一下这个Lua脚本中三段代码出现的逻辑: