暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

redisson分布式公平锁-释放锁原理

程序猿西蒙 2021-11-19
696



NO.1


前言


公平锁的客户端争抢锁的时候,如果没争抢成功,会将自己放在等待队列中,并且在超时队列放一个超时时间,当有客户端加锁时,会判断头结点是否超时需要清理,来保障客户端由于异常未及时清理阻塞住其他客户端获取锁,当持有锁的客户端释放锁之后,为了保持公平,会唤醒等待队列的头结点,去获取锁,假如这时候头结点,网络异常了,会不会被清理呢?

NO.2


原理分析

释放锁的原理依然是重写部分代码,来实现自己的lua脚本逻辑,redisson真是将lua脚本玩出花来了,那么我们继续分析lua脚本吧
while true do 
local firstThreadId2 = redis.call('lindex', KEYS[2], 0);
if firstThreadId2 == false then
break;
end;
local timeout = tonumber(redis.call('zscore', KEYS[3], firstThreadId2));
if timeout <= tonumber(ARGV[4]) then
redis.call('zrem', KEYS[3], firstThreadId2);
redis.call('lpop', KEYS[2]);
else
break;
end;
end;


if (redis.call('exists', KEYS[1]) == 0) then
local nextThreadId = redis.call('lindex', KEYS[2], 0);
if nextThreadId ~= false then
redis.call('publish', KEYS[4] .. ':' .. nextThreadId, ARGV[1]);
end;
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;
end;

redis.call('del', KEYS[1]);
local nextThreadId = redis.call('lindex', KEYS[2], 0);
if nextThreadId ~= false then
redis.call('publish', KEYS[4] .. ':' .. nextThreadId, ARGV[1]);
end;
return 1;
复制
  1. 熟悉的代码,上来就是进入循环判断头结点是否超时,是否需要进行清理,果然还是会有清理操作

    1. 为什么呢?我们在客户端加锁的时候,会进行一波清理,清除掉过期的,找到第一个不过期的节点,但是,如果这个节点之后有一部分过期了呢?怎么办?所以释放锁的时候就会进行清理操作

  2. 判断锁是否还存在,不存在则直接返回,并发布一个事件唤醒等待所的节点

  3. 对加锁次数减1,然后判断加锁数量是否还大于0

    1. 大于0,说明多次加锁(可重入的特性),那么重置锁过期时间

    2. 不大于0,说明最后一次释放锁,那么直接删除锁

  4. 获取等待队列头结点,然后唤醒它去进行加锁操作



释放锁的整体操作还是不复杂的,但是有些操作我们需要想清楚,为什么要这么实现,不实现会怎么样,有什么问题



点个在看你最好看



文章转载自程序猿西蒙,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论