Redisson分布式锁,重试锁和锁续命的原理

RedissonLock 锁重试原理

tryLock有三个三个参数,第一个是等待时间,第二个是锁失效后自动释放的时间,不填默认为-1,第三个是时间单位;

当设置了第一个参数,那这个锁就成了可重试锁;获取锁失败后,就不会立即返回了;会在等待内不断重试;如果在等待时间结束后,还没有获取到锁,那就失败了。

获取当前时间,线程ID,尝试获取锁,判断锁失效后自动释放的时间是否等于-1,如果不等于,就用自己的锁释放时间,如果等于-1,异步调用tryLockInnerAsync,返回值是个Future,第一个参数是等待时间,第二个参数是锁释放时间,看门狗的默认30秒,第三个是时间单位,第四个参数是线程ID,这个方法内是个lua脚本,成功返回空,是否返回过期时间;判断返回值是否为空,不为空,计算剩余等待时间,判断等待时间>0,大于就去尝试获取锁,但不是立即获取锁,是在剩余等待时间内订阅了锁释放的情况(锁释放的时候会发布通知),返回值也是个Future,如果超时了,会取消订阅;如果锁已经释放了,计算剩余等待时间,判断剩余等待时间>0,开始循环,就可以重新获取锁,和上面一样,如果失败,同时上,但这里用的是信号量。


lock.tryLock方法

复制代码
第一个参数(等待时间),如果设置了,获取锁失败后,就不会立即返回了;会在等待内不断重试;如果在等待时间结束后,还没有获取到锁,那就失败了。所以设置后就变成了可重试的锁了。
第二个参数(锁失效后自动释放的时间,不填默认为-1)
第三个参数(时间单位)
源码跟进

第二个参数没给,默认值-1,跟进去tryLock方法看看


跟进tryAcquire方法,尝试获取锁

跟进tryAcquireAsync方法

java 复制代码
//源码
  private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {	
 		 //判断释放锁的时间是否为-1
        if (leaseTime != -1L) {
       		 //用自己的释放锁的时间
            return this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
        } else {
        	//如果是-1,设置一个默认的释放锁的时间,30秒(getLockWatchdogTimeout)
            RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(waitTime, this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
            ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
                if (e == null) {
                    if (ttlRemaining == null) {
                        this.scheduleExpirationRenewal(threadId);
                    }

                }
            });
            return ttlRemainingFuture;
        }
    }


跟进getLockWatchdogTimeout看门狗

显示默认30秒

返回RedissonLock类,继续跟进tryLockInnerAsync方法

tryLockInnerAsync 异步方法,有没有拿到结果不清楚

看到有lua脚本

lua脚本

判断锁是否存在

不存在设置锁标识,v+1

设置有效期

存在,判断锁是不是我自己的

是,v+1

设置有效期

成功获取锁,返回空

失败,返回锁的有效期(毫秒)

java 复制代码
  <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
  		//释放锁的时间,记录到本地一个成员变量里
        this.internalLockLeaseTime = unit.toMillis(leaseTime);
        return this.evalWriteAsync(this.getName(), 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(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));
    }

一步一步往上返回,返回到RedissonLock类中的tryLock方法的tryAcquire方法,结果有两种,一种是获取锁成功返回值为空,另一种是获取锁失败返回值为锁释放的时间


继续

锁续命

当用默认的锁释放时间时,且已经拿到锁,会创建一个 ConcurrentMap来存锁的记录,以锁的名称为k,线程id为V,判断是否第一次来,如果是开启一个延时任务,10秒后执行,执行的就是更新有效期的lua脚本,更新成功后,开始递归,无限续约。

先看锁释放时间是默认值-1的情况

跟进 scheduleExpirationRenewal 方法

跟进 renewExpiration 方法

跟进 renewExpirationAsync 方法

判断锁是不是当前线程拿到的,是就重置有效期

java 复制代码
   protected RFuture<Boolean> renewExpirationAsync(long threadId) {
        return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, 
        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)
         then redis.call('pexpire', KEYS[1], ARGV[1]); 
         return 1; 
         end; 
         return 0;",
          Collections.singletonList(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));
    }

执行lua脚本后

释放锁


相关推荐
搞科研的小刘选手17 小时前
【中山大学主办】第六届计算机科学与区块链国际学术会议(CCSB 2026)
分布式·神经网络·计算机视觉·区块链·计算机科学·共识算法·自然语言
小饼干在学嘎瓦18 小时前
本地缓存和分布式缓存如何选择?
分布式·缓存
XLYcmy19 小时前
全链路验证测试系统:一个针对智能代理(Agent)系统全链路能力的自动化验证脚本
分布式·python·http·网络安全·ai·llm·agent
phltxy1 天前
HAProxy安装与RabbitMQ负载均衡配置
分布式·rabbitmq·负载均衡
jiayong231 天前
Kafka 高吞吐消息链路常见面试问题及详细解答
分布式·面试·kafka
卷毛迷你猪1 天前
快速实验篇(A2-2)数据清洗规则修正与多语言实现验证
hadoop·分布式
业精于勤_荒于稀1 天前
登录鉴权-ai
分布式
Kurisu5751 天前
深度拆解:从 CAP 定理到 Raft 协议的分布式一致性演进
分布式
kuokay1 天前
深入理解 LLM 分布式训练全栈:从硬件到 LLaMA-Factory
分布式·llama·deepspeed·fsdp·llama-factory·accelerate
Java 码思客1 天前
【Redis分布式缓存实战】第2章 Redis核心数据结构与业务实战场景
redis·分布式·缓存