一芥吟、概述
redission分布式鎖的特點有可重入获列、自動續(xù)費等逗爹,本文通過方法
RedissonLock#lock()
一步步介紹redission分布式鎖如何獲取鎖亡嫌、鎖自動續(xù)費及鎖釋放
二、獲取鎖
-
lock()
獲取鎖有兩種方式掘而,可以指定鎖的最大時長挟冠,不指定會默認最大時長為-1
方法定位:org.redisson.RedissonLock#lock()
-
設置鎖的時候,如果設置了最大有效時長會獲取鎖后直接返回袍睡,而沒有設置最大有效時長的會把時間設為看門狗配置的時間知染,默認30s,并且獲取鎖成功后會啟動看門狗斑胜,
方法定位:org.redisson.RedissonLock#tryAcquireOnceAsync
-
通過LUA腳本爭搶鎖
代碼定位:org.redisson.RedissonLock#tryLockInnerAsync
if (redis.call('exists', KEYS[1]) == 0) then -- 判斷鎖是否存在
redis.call('hincrby', KEYS[1], ARGV[2], 1); -- 保持鎖的信息,數(shù)據(jù)結(jié)構為map,并且value是自增的,value表示重入鎖的次數(shù)
redis.call('pexpire', KEYS[1], ARGV[1]); -- 設置鎖的有效期
return nil;
end; -- 返回結(jié)束
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; -- 返回結(jié)束
return redis.call('pttl', KEYS[1]); -- 如果所存在并且不是重入鎖掺炭,就直接返回鎖的有效時間
- KEYS[1]:表示自定義鎖的key, 例如:
RLock myLock = redissonClient.getLock("myLock");
, KEYS[1] 就是myLock
- ARGV[1]:表示鎖的有效期,單位是毫秒
- ARGV[2]:獲取鎖線程的唯一標識凭戴,后取用來判斷是否重入鎖
-
獲取鎖不成功會循環(huán)獲取涧狮,直到成功或中斷結(jié)束
-
看門狗啟動,延時任務遞歸實現(xiàn)自動續(xù)費
方法定位:org.redisson.RedissonLock#renewExpiration
-
看門狗通過Lua腳本續(xù)費
代碼定位:org.redisson.RedissonLock#renewExpirationAsync
- 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]); -- 重置鎖的有效時間續(xù)費
return 1; -- 續(xù)費成功返回1
end;
return 0; -- 續(xù)費失敗返回0
二、釋放鎖
- 通過Lua腳本釋放鎖
代碼定位:org.redisson.RedissonLock#unlockInnerAsync
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then -- 通過本線程的唯一標識判斷本線程的鎖是否存在
return nil; -- 不存在本線程的鎖档痪, 直接返回
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); -- 可能重入了多個鎖涉枫,先執(zhí)行value-1,去掉一個鎖
if (counter > 0) then -- 如果大于0腐螟, 表示重入鎖還沒有釋放完
redis.call('pexpire', KEYS[1], ARGV[2]); -- 重置鎖的有效期續(xù)費
return 0; -- 返回0標識
else
redis.call('del', KEYS[1]); -- 鎖已經(jīng)釋放完了愿汰,把鎖刪除
redis.call('publish', KEYS[2], ARGV[1]); -- 發(fā)布消息鎖已經(jīng)釋放
return 1; -- 返回1標識
end;
return nil;