一、概述
redission分布式鎖的特點(diǎn)有可重入、自動續(xù)費(fèi)等,本文通過方法
RedissonLock#lock()一步步介紹redission分布式鎖如何獲取鎖、鎖自動續(xù)費(fèi)及鎖釋放
二、獲取鎖
-
lock()獲取鎖有兩種方式,可以指定鎖的最大時長,不指定會默認(rèn)最大時長為-1
方法定位:org.redisson.RedissonLock#lock()
image.png -
設(shè)置鎖的時候,如果設(shè)置了最大有效時長會獲取鎖后直接返回,而沒有設(shè)置最大有效時長的會把時間設(shè)為看門狗配置的時間,默認(rèn)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); -- 保持鎖的信息,數(shù)據(jù)結(jié)構(gòu)為map,并且value是自增的,value表示重入鎖的次數(shù)
redis.call('pexpire', KEYS[1], ARGV[1]); -- 設(shè)置鎖的有效期
return nil;
end; -- 返回結(jié)束
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then -- 當(dāng)鎖存在時,判斷通過map的key是否存在,存在則是重入鎖
redis.call('hincrby', KEYS[1], ARGV[2], 1); -- 重入鎖 value自增1,代表重入鎖+1
redis.call('pexpire', KEYS[1], ARGV[1]); -- 設(shè)置鎖的有效期
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]:獲取鎖線程的唯一標(biāo)識,后取用來判斷是否重入鎖
-
獲取鎖不成功會循環(huán)獲取,直到成功或中斷結(jié)束
image.png -
看門狗啟動,延時任務(wù)遞歸實(shí)現(xiàn)自動續(xù)費(fèi)
方法定位:org.redisson.RedissonLock#renewExpiration
image.png -
看門狗通過Lua腳本續(xù)費(fèi)
代碼定位:org.redisson.RedissonLock#renewExpirationAsync
image.png
- KEYS[1]:表示自定義鎖的key, 例如:
RLock myLock = redissonClient.getLock("myLock");, KEYS[1] 就是myLock - ARGV[1]:表示鎖的有效期,單位是毫秒
- ARGV[2]:獲取鎖線程的唯一標(biāo)識,后取用來判斷是否重入鎖
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then -- 判斷鎖是否存在
redis.call('pexpire', KEYS[1], ARGV[1]); -- 重置鎖的有效時間續(xù)費(fèi)
return 1; -- 續(xù)費(fèi)成功返回1
end;
return 0; -- 續(xù)費(fèi)失敗返回0
二、釋放鎖
- 通過Lua腳本釋放鎖
代碼定位:org.redisson.RedissonLock#unlockInnerAsync
image.png
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then -- 通過本線程的唯一標(biāo)識判斷本線程的鎖是否存在
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ù)費(fèi)
return 0; -- 返回0標(biāo)識
else
redis.call('del', KEYS[1]); -- 鎖已經(jīng)釋放完了,把鎖刪除
redis.call('publish', KEYS[2], ARGV[1]); -- 發(fā)布消息鎖已經(jīng)釋放
return 1; -- 返回1標(biāo)識
end;
return nil;






