Redisson的锁重试和看门狗机制

先看一下自己利用setnx写的简易分布式锁,有四个问题,

1,不可重入

2,不可重试

3,超时自动释放(如果业务阻塞了,就算代码没执行完也会释放锁)

4,集群模式下怎么办?

复制代码
    @Override
    public boolean tryLock(long timeoutSec) {
        // 获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 获取锁
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
复制代码

Redisson都能解决!!

锁重入:

利用hash结构记录线程id和重入次数。

 

锁重试:

redisson在尝试获取锁的时候,如果传了时间参数,就不会在获取锁失败时立即返回失败,而是会进行重试。

三个参数:最大重试时间,锁释放时间(默认为-1,会触发看门狗机制),时间单位

 

上部分源码

复制代码
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
        long time = unit.toMillis(waitTime);
        long current = System.currentTimeMillis();
        long threadId = Thread.currentThread().getId();
     //尝试获取锁,返回的ttl为null,表示获取成功, Long ttl
= this.tryAcquire(waitTime, leaseTime, unit, threadId);
     //获取成功
if (ttl == null) { return true;
     //获取失败 }
else {
       //计算剩余时间 time
-= System.currentTimeMillis() - current;
        //剩余时间小于等于零,不再重试,返回失败
if (time <= 0L) { this.acquireFailed(waitTime, unit, threadId); return false; } else {
          //剩余时间大于零,subscribe订阅拿到锁的线程,该线程释放锁后会发布通知,其余等待的线程可以继续争抢。 current
= System.currentTimeMillis(); RFuture<RedissonLockEntry> subscribeFuture = this.subscribe(threadId);
复制代码

 

看门狗机制:

在获取锁成功以后,开启一个定时任务,每隔一段时间就会去重置锁的超时时间,以确保锁是在程序执行完unlock手动释放的,不会发生因为业务阻塞,key超时而自动释放的情况。

下面源码

复制代码
private void renewExpiration() {
        RedissonLock.ExpirationEntry ee = (RedissonLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());
        if (ee != null) {
       //Timeout定时任务,或者叫周期任务 Timeout task
= this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() { public void run(Timeout timeout) throws Exception { RedissonLock.ExpirationEntry ent = (RedissonLock.ExpirationEntry)RedissonLock.EXPIRATION_RENEWAL_MAP.get(RedissonLock.this.getEntryName()); if (ent != null) { Long threadId = ent.getFirstThreadId(); if (threadId != null) { RFuture<Boolean> future = RedissonLock.this.renewExpirationAsync(threadId); future.onComplete((res, e) -> { if (e != null) { RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", e); } else { if (res) { RedissonLock.this.renewExpiration(); } } }); } } }
        //刷新周期,
this.internalLockLeaseTime / 3L, 默认释放时间是30秒,除以3就是每10秒更新一次
}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS); ee.setTimeout(task); } }
复制代码

 

可以配合下面流程图来看源码

 

 

 

集群问题:

实际生产过程中,redis都是主从或者集群配置的,如果只在主节点上加锁,之后主节点宕机了,而且锁还没被同步到从节点上,就会出现安全问题。

需要使用multilock()方法。必须在所有的节点都获取锁成功,才算成功。 缺点是运维成本高,实现复杂。

复制代码
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedissonClient2 redissonClient2;
    @Resource
    private RedissonClient3 redissonClient3;

    RLock lock = redissonClient.getMultilock(lock1,lock2,lock3)

复制代码

 

posted @   wwwwwwwty  阅读(943)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示