Redisson锁源码详解

Redission获取锁

看下面一段代码,是Redisson获取锁最简单的实现方式

Java 复制代码
// 从Redisson客户端获取RLock对象
RLock lock = redissonClient.getLock(key);
// 使用RLock对象中的tryLock方法获取锁
boolean tryLock = lock.tryLock();
// 判断是否回去锁成功,!true = fase ,!false = true
if (!tryLock){
   return;
}
// 若获取锁成功则释放锁
lock.unlock();

补药小看这一段代码,Redisson提供了非常完善的锁实现的解决方案,让我们一点一点体会这段代码的美妙。

Redsson getLock()方法

我们点进getLock()方法,我们会看到这样一段代码

这段代码的意思就是new了一个RLock的实现类RedissonLock()返回了。其中name就是我们传入的key,也就是锁的key。那么connectionManager.getCommandExecutor()这是个什么东西呢?我们从字面意思可以得知这个是获取了一个命令执行器,那这个执行器是干啥的呢?

点进getCommandExecutor()方法

我们可以看到getCommandExecutor()方法是直接返回了CommandSyncService这个叫做命令同步服务的对象,那这个命令同步服务是干什么的呢? 点看进来看到CommandSyncService类继承了CommandAsyncService类,同时实现了CommandExecutor接口

看一下这里实现的方法,我们可以大概知道这个类是干什么的,CommandSyncService类是执行Redis同步命令的实现类。

Redisson tryLock()方法

我们点进去tryLock方法,我们可以发现tryLock方法有三个,分别是无参、两个参数和三个参数的。

tryLock()无参

我们先看无参的tryLock方法,首先进入tryLockAsync方法, 我们发现,tryLockAsync空参方法,其实就是调用了tryLockAsync有参方法,我们点进有参方法。 我们发现,在之前的tryLock方法中我们没有设置过期时间和等待时间,在这里都传入了-1,这里的-1的作用,我们后面再说。我们现在点进去tryAcquireOnceAsync方法看一下。 这治理我们可以看到,若是过期时间不为-1,则直接开始尝试获取锁,若过期时间为-1,则我们就设置默认的过期时间 30 * 1000 毫秒。在尝试锁成功之后调用scheduleExpirationRenewal方法安排锁的过期时间续期任务。 我们现在点进tryLockInnerAsync方法,看看Redisson是怎么获取锁的,这里我们选择RedissonLock的包。 我们看到这里其实就是使用lua脚本获取锁。

tryLock()三个参数

Java 复制代码
@Override
    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();
        Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
        // lock acquired
        if (ttl == null) {
            return true;
        }
        
        time -= System.currentTimeMillis() - current;
        if (time <= 0) {
            acquireFailed(waitTime, unit, threadId);
            return false;
        }
        
        current = System.currentTimeMillis();
        RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
        if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
            if (!subscribeFuture.cancel(false)) {
                subscribeFuture.onComplete((res, e) -> {
                    if (e == null) {
                        unsubscribe(subscribeFuture, threadId);
                    }
                });
            }
            acquireFailed(waitTime, unit, threadId);
            return false;
        }

        try {
            time -= System.currentTimeMillis() - current;
            if (time <= 0) {
                acquireFailed(waitTime, unit, threadId);
                return false;
            }
        
            while (true) {
                long currentTime = System.currentTimeMillis();
                ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
                // lock acquired
                if (ttl == null) {
                    return true;
                }

                time -= System.currentTimeMillis() - currentTime;
                if (time <= 0) {
                    acquireFailed(waitTime, unit, threadId);
                    return false;
                }

                // waiting for message
                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(waitTime, unit, threadId);
                    return false;
                }
            }
        } finally {
            unsubscribe(subscribeFuture, threadId);
        }
//        return get(tryLockAsync(waitTime, leaseTime, unit));
    }

这段代码大体上的执行流程是 这里我们注意一下,如果过期时间是-1的话,我们是会走watchDog续约机制的。什么是watchDog续约机制,我下一章专门讲一下。 我们现在把关键的方法说一下

tryAcquire()

点进去,我们发现和之前的空参一样的,只是这里的参数都是我们之前设置的不再是-1了。 我们进入tryAcquireAsync方法 我们会发现,由于我们走的是自己设置的过期时间,所以我们这次会走if中的代码,而不是下面的代码 上面和下面代码的差别就在于,leaseTime和waitTime。

tryLock()两个个参数

点进方法我们就可以看到,其实还是调用了tryLock方法,只是将过期时间设置为了-1而已。

Redission释放锁

我们点进unLock()方法,选择RedissonLock包下的unLock()方法 这里其实就只有一段核心代码,就是get(unlockAsync(Thread.currentThread().getId())); 这段代码是什么意思呢?我们先看 unlockAsync()方法。 这段代码其实就是异步解锁,然后判断解锁是否成功的 点进unlockInnerAsync方法,选择Redisson包下的 我们可以看到这其实就是一段lua代码去redis中尝试解锁。 这段代码就是对lua脚本返回的结果进行处理判断的逻辑,在解锁完成之后调用cancelExpirationRenewal取消续约任务,如果在解锁过程之中发生异常,就记录异常,如果解锁失败(当前线程未持有锁)就抛出异常,解锁成功就标记操作成功。

相关推荐
都叫我大帅哥2 小时前
Redis AOF持久化深度解析:命令日志的终极生存指南
redis
都叫我大帅哥2 小时前
Redis RDB持久化深度解析:内存快照的魔法与陷阱
redis
Hello.Reader6 小时前
Redis 延迟监控深度指南
数据库·redis·缓存
ybq195133454316 小时前
Redis-主从复制-分布式系统
java·数据库·redis
马里奥Marioぅ6 小时前
Redis主从切换踩坑记:当Redisson遇上分布式锁的“死亡连接“
redis·分布式锁·redisson·故障转移
Hello.Reader11 小时前
Redis 延迟排查与优化全攻略
数据库·redis·缓存
东窗西篱梦18 小时前
Redis集群部署指南:高可用与分布式实践
数据库·redis·分布式
半新半旧19 小时前
Redis集群和 zookeeper 实现分布式锁的优势和劣势
redis·分布式·zookeeper
@ chen21 小时前
Redis事务机制
数据库·redis
静若繁花_jingjing1 天前
Redis线程模型
java·数据库·redis