Redisson分布式锁流程分析(3)可重入锁互斥

在前面可重入锁加锁一文中,我们可以知道当锁存在时,是会加锁失败的,即 Hash 的 key 存在, field 也存在,但 field 对应的 value 不是当前线程生成的这种情况。

所以本文看一下这种加锁失败的情况Redisson会怎样处理呢?

再看加锁 Lua 脚本

1
2
3
4
5
6
7
8
9
10
11
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
return redis.call('pttl', KEYS[1]);

在 lua 脚本中,前两段 if 分别排除了两种情况:

  1. 锁不存在;
  2. 锁存在且是自己线程(可重入);

剩下的情况就是锁存在,但是不是自己线程锁,也就意味着加锁失败。

对于这种情况,会执行 pttl 命令,返回锁的剩余时间。

加锁失败后的处理

查看RedissonLock类中lock(long leaseTime, TimeUnit unit, boolean interruptibly)这个方法的源码。

先来看开头一部分:返回值ttl若为null,说明加锁成功,方法直接返回。

再看下面获取锁失败之后的逻辑:在一个while (true)循环里一直调用 tryAcquire 方法,直到加锁成功!

总结

  1. 可重入锁的互斥是依靠 Redis Lua 脚本来保证的;
  2. 加锁失败会返回当前锁的剩余时间;
  3. 加锁失败后,会在 Java 代码中使用 while 循环一直尝试加锁。

大概的流程,如下图:

------ 本文完 ------