分布式锁Redisson浅谈RedissonLock

公司的项目需要用的分布式锁 redisson的版本


        
            org.redisson
            redisson-spring-boot-starter
            3.12.2
        

问题: 公司的服务是分布式的, 4台服务器 。 4台服务器上都有定时任务的代码.导致到了规定时间点4个服务会同时运行就会引发并发问题. 然后加上 了redis 的分布式锁 。 通过redisson实现 , 但是问题并没有完全解决,同一时间点4个任务并发的问题没了又出现了 4个任务接连执行 ,为什么会这样呢 ? 这个现象加上我经验我有理由怀疑时redisson中获取锁时会进行轮循知道获取锁成功,带着疑问和猜想 我去看了看源码。

原来逻辑:

我使用的redis客户端是RedissonClient 的实现类redisson 通过getLock() 获取锁对象

org.redisson.Redisson //客户端实现类
//获取锁对象的方法

    @Override
    public RLock getLock(String name) {
        return new RedissonLock(connectionManager.getCommandExecutor(), name);
    }
    

这里获取的锁对象可以看到是Lock的实现类RedissonLock

然后调用尝试持有锁lock.tryLock(long time,long time,TimeUnit unit) ,如果获取成功就会执行定时任务的逻辑


                if (lock.tryLock(100, 60 * 1000, TimeUnit.MILLISECONDS)) {
                    //业务代码
                }

业务代码内有打日志,发现业务代码执行了4次,只有一个是在设定时间启动,其他都或多或少的有些延后,而我的定时任务的执行时间是大于100ms的

很明显能够看出是获取锁的时候出了问题

所以就从tryLock方法这里开始,我加了自己理解的注释

 @Override
        public boolean tryLock ( long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
            //将 waitTime 毫秒化,  即将单位统一 
            long time = unit.toMillis(waitTime);
            //获取当前的时间戳
            long current = System.currentTimeMillis();
            long threadId = Thread.currentThread().getId();
            //第一次尝试加锁
            Long ttl = tryAcquire(leaseTime, unit, threadId);
            // lock acquired
            // 判断获取锁是否成功
            if (ttl == null) {
                return true;
            }
            //第一次获取锁锁失败,判断获取消耗时间是否大于等于waitTime
            time -= System.currentTimeMillis() - current;
            if (time <= 0) {
                //超过等待时间返回false
                acquireFailed(threadId);
                return false;
            }
            //下边的代码不用看太深, 我们的问题肯定是有循环参与的,重新获取的时间戳更新current
            current = System.currentTimeMillis();
            RFuture subscribeFuture = subscribe(threadId);
            if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
                if (!subscribeFuture.cancel(false)) {
                    subscribeFuture.onComplete((res, e) -> {
                        if (e == null) {
                            unsubscribe(subscribeFuture, threadId);
                        }
                    });
                }
                acquireFailed(threadId);
                return false;
            }
            
            //又判断了一次上边代码执行时间是否超过等待时间
            try {
                time -= System.currentTimeMillis() - current;
                if (time <= 0) {
                    acquireFailed(threadId);
                    return false;
                }
                //重头戏来了 。  看到这个while(true)就感觉输光已经不远了
                while (true) {
                    //再次获取当前时间戳
                    long currentTime = System.currentTimeMillis();
                    ttl = tryAcquire(leaseTime, unit, threadId);
                    // lock acquired
                    // 判断是否获取锁成功
                    if (ttl == null) {
                        return true;
                    }
                    //判断获取锁时间是否超过等待时间
                    time -= System.currentTimeMillis() - currentTime;
                    if (time <= 0) {
                        acquireFailed(threadId);
                        return false;
                    }

                    // waiting for message
                    // 再一次刷新currentTime的值为当前时间戳
                    currentTime = System.currentTimeMillis();
                    if (ttl >= 0 && ttl < time) {
                        subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    } else {
                        subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
                    }
                    //判断所耗时间是否大于等待时间
                    time -= System.currentTimeMillis() - currentTime;
                    if (time <= 0) {
                        acquireFailed(threadId);
                        return false;
                    }
                }
            } finally {
                //取消操作
                unsubscribe(subscribeFuture, threadId);
            }
//        return get(tryLockAsync(waitTime, leaseTime, unit));
        }

从看源码可以知道,只要在while 之前没有返回 这个方法就会保证能成功获取锁对象, 只是时间问题.

但我希望就是4个有一个定时任务跑就行了 不需要每个都起来. 怎么办? 很简单让等待时间为0 。 就相当于不等待 。如果第一次获取锁对象失败就放弃 。 就ok 。

另外lock 是有一个isLock() 的方法来看锁是不是已经锁上了 。 但是如果先判断然后再决定是否尝试持有锁的话就会有并发问题 。 哈哈哈 。 细品下

 

 

你可能感兴趣的:(Thread,Redis,Redisson)