redisson中的看门狗机制

redis分布式锁演示代码:

public String hello() throws InterruptedException{
        //获取一把锁,名称相同,就是同一把锁
        RLock lock = redisson.getLock("my-lock");
        //lock.lock();
        lock.lock(10, TimeUnit.SECONDS);//自动解锁时间,一定要大于业务的执行时间
        //问题lock.lock(10, TimeUnit.SECONDS)在锁时间到了之后,不会自动续期
        try {
            System.out.println("加锁成功!");
            Thread.sleep(3000);
        }finally {
            lock.unlock();
        }

        return "hello";
    }
  1. lock.lock();此为一个阻塞式锁,默认锁的过期时间为30s,如果30s内业务代码还没有执行完,将在1/3看门狗时间后自动续期;
  2. lock.lock(10, TimeUnit.SECONDS);当我们使用此方法指定过期时间后,便又引发了一个问题, 当业务逻辑执行时间超过10s时,锁已经释放,此时如果还有其他线程访问,又可以得到锁; 且如上述代码中:
		try {
            System.out.println("加锁成功!");
            Thread.sleep(3000);
        }finally {
            lock.unlock();
        }

当业务逻辑在30s执行完成后,他删的锁是下一个线程的锁,而不是它自己的锁;此时便会报出如下错误:redisson中的看门狗机制_第1张图片
2,接下来,我们看一下他的源代码:
redisson中的看门狗机制_第2张图片
可以看到,当我们指定时间后,他会用我们指定的时间再次执行lock方法;
redisson中的看门狗机制_第3张图片
首先他会获取线程Id, 接着如果获取到锁,就直接返回,没有获取到就循环等待, 直至获取到锁;
我们再来看看tryAcquire方法:
redisson中的看门狗机制_第4张图片
他又调用了get方法, get方法里面又调用了tryAcquireAsync方法;

private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {
        if (leaseTime != -1) {
            return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
        }
        RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
        ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
            if (e != null) {
                return;
            }

            // lock acquired
            if (ttlRemaining == null) {
                scheduleExpirationRenewal(threadId);
            }
        });
        return ttlRemainingFuture;
    }

leaseTime则是我们指定的10s, 如果没有传时间,他会调用如下lock方法,将时间设置为-1;
redisson中的看门狗机制_第5张图片

传时间

调用tryLockInnerAsync函数
redisson中的看门狗机制_第6张图片
此方法是向redis发送一个lua脚本去占位执行,

未传时间

private RFuture<Boolean> tryAcquireOnceAsync(long leaseTime, TimeUnit unit, long threadId) {
        if (leaseTime != -1) {
            return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
        }
        RFuture<Boolean> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
        ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
            if (e != null) {
                return;
            }

            // lock acquired
            if (ttlRemaining) {
                scheduleExpirationRenewal(threadId);
            }
        });
        return ttlRemainingFuture;
    }

它便会获取锁的看门狗时间getLockWatchdogTimeout()
redisson中的看门狗机制_第7张图片
可以看到,看门狗默认时间为30s; 如果占位成功! 就会继续监听
redisson中的看门狗机制_第8张图片
有异常直接返回, 没有异常根据代码,我们可以看到他将重新获取过期时间,
redisson中的看门狗机制_第9张图片
在renewExpiration方法中:
redisson中的看门狗机制_第10张图片
定时任务中的renewExpirationAsync()方法:
redisson中的看门狗机制_第11张图片
又是向redis发送lua脚本执行; 且它的internalLockLeaseTime又是看门狗时间,如下:
redisson中的看门狗机制_第12张图片
续期时间:
redisson中的看门狗机制_第13张图片
结论:

  • 如果我们传了时间,就发送给redis执行脚本, (如果不解锁)锁将在我们指定的时间内释放
  • 如果未指定锁的超时时间, 就使用30 * 1000看门狗时间

推荐使用传时间的lock方法,过期时间不要毫秒级别即可!

lock.lock(10, TimeUnit.SECONDS);//省掉续期时间

新开了公众号,欢迎大家关注交流,互相讨论!
redisson中的看门狗机制_第14张图片

你可能感兴趣的:(其他,redis,java,数据库)