【redisson】为什么不同服务实例下生成的线程id和锁key一样,却不是同一把锁

(着重讲可重入,不同服务实例下生成的线程id和锁key一样,此时锁不支持重入)

我们在使用redisson可重入锁时,都会了解到一点:可重入是通过线程id是否相同实现的。

ps:若锁住的代码块执行完就会解锁,不会涉及到可重入性。若锁住的代码块执行完不解锁,需要延迟(过期自动释放),刚刚加锁的线程已经在等待执行下一个任务,如果此线程再次执行到该加锁代码块,即使未解锁,此线程也能再次获取到锁(重入性)。

分析下redisson获取锁的源码:

private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    // threadId 线程id
    return get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    RFuture<Long> ttlRemainingFuture;
    if (leaseTime > 0) {

        // 锁固定了过期时间
        ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
    } else {

        //此处使用看门狗,默认30s过期,可通过配置修改
        ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,
                TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
    }
    CompletionStage<Long> f = ttlRemainingFuture.thenApply(ttlRemaining -> {
        // lock acquired
        if (ttlRemaining == null) {
            if (leaseTime > 0) {
                internalLockLeaseTime = unit.toMillis(leaseTime);
            } else {
                scheduleExpirationRenewal(threadId);
            }
        }
        return ttlRemaining;
    });
    return new CompletableFutureWrapper<>(f);
}
<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) " +
                        "or (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()),

// getLockName(threadId) 通过threadId获取锁标识。用于二次判断(可重入的关键)

unit.toMillis(leaseTime), getLockName(threadId));
}
protected String getLockName(long threadId) {

    //从这里可以看到锁标识还包含了一个id,问题是这个id是如何生成的,有何作用?
    return id + ":" + threadId;
}
public RedissonBaseLock(CommandAsyncExecutor commandExecutor, String name) {
    super(commandExecutor, name);
    this.commandExecutor = commandExecutor;

    //接着往前推,能猜到一点,这个id肯定是redisson初始化连接时赋值的
    this.id = commandExecutor.getConnectionManager().getId();
    this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout();
    this.entryName = id + ":" + name;
}

问题已经很清晰了,接着看下redisson是如何初始化的。

我这边用的是:redisson-spring-boot-starter,入口是:org.redisson.spring.starter.RedissonAutoConfiguration#redisson()

初始化流程:

创建连接管理器,代码第一行:

UUID id = UUID.randomUUID();

往下逻辑处理的时候都会把id当作入参。所以redisson在不同服务实例下生成的id是不一样的。

Redisson获取锁,在调用getLockName(long threadId)方法时,即使线程id一样,在不同的服务实例下,返回的结果也是不相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值