Redisson分布式锁源码篇

☆* o(≧▽≦)o *☆嗨~我是小奥🍹
📄📄📄个人博客:小奥的博客
📄📄📄CSDN:个人CSDN
📙📙📙Github:传送门
📅📅📅面经分享(牛客主页):传送门
🍹文章作者技术和水平有限,如果文中出现错误,希望大家多多指正!
📜 如果觉得内容还不错,欢迎点赞收藏关注哟! ❤️

Redisson分布式锁源码篇

Redisson 是一个简单的Redis Java客户端,具有内存数据网格功能。

它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。

GitHub地址:https://github.com/redisson/redisson

Redisson分布式锁不仅是相对成熟的分布式锁方案,而且在很多企业中都会去使用的,所以了解一下底层的实现还是很有必要的。

一、使用Redisson分布式锁

Redisson分布式锁的使用非常简单,我们只需要在maven中引入依赖,然后直接调用相关API即可。

1.1 引入依赖

		<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.6</version>
        </dependency>

1.2 调用API

		// 创建RedissonClient
        RedissonClient redissonClient = Redisson.create();
        // 创建分布式锁
        RLock lock = redissonClient.getLock("lock");
        // 加锁
        lock.lock();
        // 尝试获取锁,无参表示不等待立即返回
        boolean b = lock.tryLock();
        // 解锁
        lock.unlock();

二、源码解析

下面我就带大家去分析一下Redisson的可重入和可重试的源码。

2.1 可重入锁原理

(1) 原理解释

Redisson分布式锁的可重入锁的原理:

  • 使用Redis中Hash的key-value结构,存储线程标识重入次数
  • 每次获取锁的时候,如果线程标识相同,就给Hash的value加1
  • 每次释放锁的时候,并不会删除锁,而是重入次数减1
  • 当所有逻辑执行完,如果重入次数为0,则删除锁

逻辑流程图

在这里插入图片描述

(2) 源码分析

① 尝试获取锁
	// 尝试获取锁,无参表示不等待立即返回
    boolean b = lock.tryLock();

org.redisson.RedissonLock

	@Override
    public boolean tryLock() {
   
        return get(tryLockAsync());
    }
    @Override
    public RFuture<Boolean> tryLockAsync() {
   
        return tryLockAsync(Thread.currentThread().getId());
    }
    @Override
    public RFuture<Boolean> tryLockAsync(long threadId) {
   
        // tryAcquireOnceAsync有三个参数 
        // long waitTime 锁重试的最大等待时间
        // long leaseTime 锁自动释放时间
        // TimeUnit unit 时间单位 
        //  long threadId 线程id
        return tryAcquireOnceAsync(-1, -1, null, threadId);
    }

注意,下面的代码我们只需要关注有注释的部分,剩余的部分会在后面讲到。

    // 根据tryAcquireOnceAsync命名可知,这里获取锁只会获取一次,不会进行重试
	private RFuture<Boolean> tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
   
        // 判断leaseTime是否为-1(leaseTime为锁自动释放的时间,如果不传默认为-1)
        if (leaseTime != -1) {
   
            return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
        }
        // tryLockInnerAsync是尝试获取锁的方法
        RFuture<Boolean> ttlRemainingFuture = tryLockInnerAsync(waitTime,
                                                    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;
    }

tryLockInnerAsync()方法是获取锁的主要实现,我们可以看到,其实都是固定的Lua脚本逻辑。

    // 尝试获取锁的逻辑(重点)
	<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
   
        internalLockLeaseTime = unit.toMillis(leaseTime);

        return evalWriteAsync(getName(), LongCodec.INSTANCE, command,
                "if (redis.call('exists', KEYS[1]) == 0) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                        "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return nil; " +
                        "end; " +
                        "return redis.call('pttl', KEYS[1]);",
                Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
    }

我们把这段Lua脚本摘出来分析一下逻辑:

    -- 判断锁是否存在
    if (redis.call('exists', KEYS[1]) == 0) then 
        -- 等于0表示不存在,创建锁,设置过期时间
        redis
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值