一、概述
redission分布式锁的特点有可重入、自动续费等,本文通过方法
RedissonLock#lock()
一步步介绍redission分布式锁如何获取锁、锁自动续费及锁释放
二、获取锁
-
lock()
获取锁有两种方式,可以指定锁的最大时长,不指定会默认最大时长为-1
方法定位:org.redisson.RedissonLock#lock()
image.png
-
设置锁的时候,如果设置了最大有效时长会获取锁后直接返回,而没有设置最大有效时长的会把时间设为看门狗配置的时间,默认30s,并且获取锁成功后会启动看门狗,
方法定位:org.redisson.RedissonLock#tryAcquireOnceAsync
image.png
-
通过LUA脚本争抢锁
代码定位:org.redisson.RedissonLock#tryLockInnerAsync
image.png
if (redis.call('exists', KEYS[1]) == 0) then -- 判断锁是否存在
redis.call('hincrby', KEYS[1], ARGV[2], 1); -- 保持锁的信息,数据结构为map,并且value是自增的,value表示重入锁的次数
redis.call('pexpire', KEYS[1], ARGV[1]); -- 设置锁的有效期
return nil;
end; -- 返回结束
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then -- 当锁存在时,判断通过map的key是否存在,存在则是重入锁
redis.call('hincrby', KEYS[1], ARGV[2], 1); -- 重入锁 value自增1,代表重入锁+1
redis.call('pexpire', KEYS[1], ARGV[1]); -- 设置锁的有效期
return nil;
end; -- 返回结束
return redis.call('pttl', KEYS[1]); -- 如果所存在并且不是重入锁,就直接返回锁的有效时间
- KEYS[1]:表示自定义锁的key, 例如:
RLock myLock = redissonClient.getLock("myLock");
, KEYS[1] 就是myLock
- ARGV[1]:表示锁的有效期,单位是毫秒
- ARGV[2]:获取锁线程的唯一标识,后取用来判断是否重入锁
-
获取锁不成功会循环获取,直到成功或中断结束
image.png
-
看门狗启动,延时任务递归实现自动续费
方法定位:org.redisson.RedissonLock#renewExpiration
image.png
-
看门狗通过Lua脚本续费
代码定位:org.redisson.RedissonLock#renewExpirationAsync
image.png
- KEYS[1]:表示自定义锁的key, 例如:
RLock myLock = redissonClient.getLock("myLock");
, KEYS[1] 就是myLock
- ARGV[1]:表示锁的有效期,单位是毫秒
- ARGV[2]:获取锁线程的唯一标识,后取用来判断是否重入锁
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then -- 判断锁是否存在
redis.call('pexpire', KEYS[1], ARGV[1]); -- 重置锁的有效时间续费
return 1; -- 续费成功返回1
end;
return 0; -- 续费失败返回0
二、释放锁
- 通过Lua脚本释放锁
代码定位:org.redisson.RedissonLock#unlockInnerAsync
image.png
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then -- 通过本线程的唯一标识判断本线程的锁是否存在
return nil; -- 不存在本线程的锁, 直接返回
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); -- 可能重入了多个锁,先执行value-1,去掉一个锁
if (counter > 0) then -- 如果大于0, 表示重入锁还没有释放完
redis.call('pexpire', KEYS[1], ARGV[2]); -- 重置锁的有效期续费
return 0; -- 返回0标识
else
redis.call('del', KEYS[1]); -- 锁已经释放完了,把锁删除
redis.call('publish', KEYS[2], ARGV[1]); -- 发布消息锁已经释放
return 1; -- 返回1标识
end;
return nil;
网友评论