Redission是通过lua脚本来访问Redis来确保业务逻辑执行的原子性的。
以下就是Redission中lua加锁的代码
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hset', 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]);
KEYS[1]:就代表加的那把锁的key,我只需要判断这个key值是否存在就能知道锁是否被线程持有。
ARGV[1]:表示锁的有效期,默认30s
ARGV[2]:表示表示加锁的客户端ID
首先先判断这把锁的key值是否存在,如果不存在,那就可以直接加锁。如果已存在,就要判断一下持有锁的线程是不是当前线程。所以用hexist来判断这个hash中是否存在当前线程的ID,如果存在就说持有锁的就是当前线程,则可以再次进入。将value值加1并延长锁的有效时间。如果不是当前线程的ID,那么就会返回剩余的生存时间,当前线程就会进入一个循环,不断的去尝试获取锁。
以下是Redisson释放锁的lua代码
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
return nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) then
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0;
else redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
return nil;
key 不存在,说明锁已释放,直接执行 publish 命令发布释放锁消息并返回 1。
key 存在,但是 field 在 Hash 中不存在,说明自己不是锁持有者,无权释放锁,返回 nil。
因为锁可重入,所以释放锁时不能把所有已获取的锁全都释放掉,一次只能释放一把锁,因此执行 hincrby 对锁的值减一。
释放一把锁后,如果还有剩余的锁,则刷新锁的失效时间并返回 0;如果刚才释放的已经是最后一把锁,则执行 del 命令删除锁的 key,并发布锁释放消息,返回 1。
网友评论