redisson的可重入锁原理

  1. 获取锁
 <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('hincrby', 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]);",
                Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
    }
-- 如果键名为 KEYS[1] 的键不存在
if (redis.call('exists', KEYS[1]) == 0) then
    -- 将键名为 KEYS[1] 的哈希表中 ARGV[2] 字段的值加上 1
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    -- 设置键名为 KEYS[1] 的过期时间为 ARGV[1] 毫秒
    redis.call('pexpire', KEYS[1], ARGV[1]);
    -- 返回 nil
    return nil;
end;
-- 如果键名为 KEYS[1] 的哈希表中存在 ARGV[2] 字段
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    -- 将键名为 KEYS[1] 的哈希表中 ARGV[2] 字段的值加上 1
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    -- 设置键名为 KEYS[1] 的过期时间为 ARGV[1] 毫秒
    redis.call('pexpire', KEYS[1], ARGV[1]);
    -- 返回 nil
    return nil;
end;
-- 返回键名为 KEYS[1] 的键的剩余过期时间(以毫秒为单位)
return redis.call('pttl', KEYS[1]);

这段代码的作用是:如果不存在指定的键(分布式锁),则创建该键并将指定字段的值加1,设置过期时间并返回 nil;如果存在指定的键和字段,则将该字段的值加1并更新过期时间,返回 nil;如果存在指定的键但不存在指定的字段,则返回键的剩余过期时间(以毫秒为单位)。
2. 释放锁

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
    return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            "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;",
            Arrays.asList(getRawName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}
-- 如果键名为 KEYS[1] 的哈希表中不存在字段 ARGV[3]
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
    -- 返回 nil
    return nil;
end;
-- 将键名为 KEYS[1] 的哈希表中字段 ARGV[3] 的值减去 1
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
-- 如果哈希表中字段 ARGV[3] 的值仍然大于 0
if (counter > 0) then
    -- 设置键名为 KEYS[1] 的过期时间为 ARGV[2] 毫秒
    redis.call('pexpire', KEYS[1], ARGV[2]);
    -- 返回 0
    return 0;
else
    -- 删除键名为 KEYS[1] 的键
    redis.call('del', KEYS[1]);
    -- 向频道 KEYS[2] 发布消息 ARGV[1]
    redis.call('publish', KEYS[2], ARGV[1]);
    -- 返回 1
    return 1;
end;
-- 返回 nil
return nil;

这段代码的作用是:首先判断指定的键和字段是否存在,如果不存在则返回 nil;如果存在,则将指定字段的值减1,并判断减1后是否大于0,如果大于0则设置键的过期时间并返回0,表示锁已被释放但仍有其他客户端持有;如果减1后的结果小于等于0,则删除该键并向指定频道发布消息,表示锁已被完全释放,返回1。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值