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";
}
lock.lock();
此为一个阻塞式锁,默认锁的过期时间为30s,如果30s内业务代码还没有执行完,将在1/3看门狗时间后自动续期;lock.lock(10, TimeUnit.SECONDS);
当我们使用此方法指定过期时间后,便又引发了一个问题, 当业务逻辑执行时间超过10s时,锁已经释放,此时如果还有其他线程访问,又可以得到锁; 且如上述代码中:
try {
System.out.println("加锁成功!");
Thread.sleep(3000);
}finally {
lock.unlock();
}
当业务逻辑在30s执行完成后,他删的锁是下一个线程的锁,而不是它自己的锁;此时便会报出如下错误:
2,接下来,我们看一下他的源代码:
可以看到,当我们指定时间后,他会用我们指定的时间再次执行lock方法;
首先他会获取线程Id, 接着如果获取到锁,就直接返回,没有获取到就循环等待, 直至获取到锁;
我们再来看看tryAcquire方法:
他又调用了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;
传时间
调用tryLockInnerAsync函数
此方法是向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()
可以看到,看门狗默认时间为30s; 如果占位成功! 就会继续监听
有异常直接返回, 没有异常根据代码,我们可以看到他将重新获取过期时间,
在renewExpiration方法中:
定时任务中的renewExpirationAsync()方法:
又是向redis发送lua脚本执行; 且它的internalLockLeaseTime又是看门狗时间,如下:
续期时间:
结论:
- 如果我们传了时间,就发送给redis执行脚本, (如果不解锁)锁将在我们指定的时间内释放
- 如果未指定锁的超时时间, 就使用30 * 1000看门狗时间
推荐使用传时间的lock方法,过期时间不要毫秒级别即可!
lock.lock(10, TimeUnit.SECONDS);//省掉续期时间
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/96842.html