系列博客目录



https://blog.csdn.net/2303_76696898/article/details/142426882

怎么样才能够在简历上写熟悉redisson的应用以及原理

在简历上描述你熟悉 Redisson 的应用以及原理时,需要做到以下几点:

1. 清晰描述技能与经验

直接说明你对 Redisson 的熟悉程度以及具体的应用场景,展示你掌握的技术能力和应用经验。

示例:
  • 核心技能描述

    熟悉 Redisson 框架,掌握其核心功能(如分布式锁、缓存、延时队列等)的应用场景及实现原理。


2. 列举具体应用场景

通过实际的项目经验或典型场景来证明你熟悉 Redisson。以下是一些可以写在简历上的内容:

示例项目经验:
  1. 分布式锁

    在项目中使用 Redisson 实现分布式锁,解决多节点环境下资源竞争问题,保证任务调度的幂等性。通过 RLock 提高系统的稳定性,并避免因重复操作导致的数据冲突。

  2. 分布式缓存

    使用 Redisson 实现基于 Redis 的分布式缓存,结合 Spring Boot 提供缓存自动刷新功能,优化热点数据的访问性能。

  3. 延时任务(分布式队列)

    利用 Redisson 的 RDelayedQueue 实现延时任务调度,用于电商订单超时取消功能,减少订单未支付资源浪费。

  4. 分布式对象

    使用 Redisson 提供的分布式对象(如 RMapRSet 等),实现多节点间共享数据一致性,避免传统分布式缓存的复杂性。

  5. 事务与锁结合

    在复杂交易系统中,结合 Redisson 分布式锁与 Redis 事务,保证高并发场景下的数据一致性和安全性。


3. 展示你对原理的理解

除了应用,还需要说明你对 Redisson 核心原理的理解。以下是常见的原理,可以根据你的实际掌握情况选取一些写入简历:

示例:
  • Redisson 的核心机制

    理解 Redisson 通过基于 Redis 的 Lua 脚本实现分布式锁的可重入性、自动续期及超时释放机制,掌握其背后的实现逻辑。

  • 分布式锁实现原理

    熟悉 Redisson 分布式锁的实现原理,包括:

    • 单实例模式:基于 Redis 单节点的锁机制。
    • 集群模式:通过主从复制和集群广播机制,实现分布式环境下的高可用锁管理。
  • 一致性与高可用保障

    熟悉 Redisson 基于 Redis 哨兵模式和集群模式实现的高可用分布式锁机制,了解锁失效和数据一致性问题的处理。

  • 缓存设计原理

    掌握 Redisson 基于本地缓存与 Redis 缓存结合的二级缓存策略,避免缓存击穿与缓存雪崩问题。

  • 延时任务的实现

    理解 Redisson 的延时队列 RDelayedQueue 背后的时间轮算法(time-wheel)实现原理,以及其在高性能任务调度中的优势。


4. 用简历中的关键词突出你的能力

招聘方通常通过关键字筛选简历,所以确保简历中包含以下关键词:

  • 分布式锁
  • 缓存机制
  • 延时队列
  • Redisson
  • Redis
  • 数据一致性
  • 高并发
  • 高可用
示例段落:

熟悉 Redisson 框架,具备丰富的分布式环境下的开发经验。在电商项目中,基于 Redisson 实现分布式锁(RLock)和延时任务(RDelayedQueue),解决高并发环境下的资源竞争与任务调度问题。掌握其核心原理,包括锁的可重入性、自动续期、高可用保障机制及其在 Redis 哨兵与集群模式下的实现逻辑。


5. 附加具体的技术成果

如果可能,可以在简历中添加具体的技术成果或优化效果。例如:

  • 性能提升:描述通过使用 Redisson 优化后的系统性能。
    • 示例:通过 Redisson 分布式锁优化任务调度系统,减少 40% 的任务冲突,提升任务处理效率约 30%。
  • 系统稳定性:通过分布式锁、缓存等应用,解决实际的复杂问题。
    • 示例:通过 Redisson 缓存机制降低数据库查询压力,支撑了日均 500w+ 的请求量。

总结

在简历中写 “熟悉 Redisson 的应用及原理” 时,可以采用以下结构:

  1. 技能概述:直接说明熟悉 Redisson 的具体功能(如分布式锁、缓存、延时队列)和实现原理。
  2. 项目经验:列举 2~3 个具体的项目场景,描述问题、Redisson 的应用方式及最终优化效果。
  3. 技术理解:补充对核心原理的掌握(如分布式锁、缓存一致性、高可用机制等)。
  4. 成果数据:用实际成果证明你的能力(如性能提升、系统稳定性提高等)。

通过具体化的描述,不仅可以证明你对 Redisson 的熟悉程度,还能体现出你对分布式系统的实战经验。

1.为什么学习Redisson

在这里插入图片描述
为了解决上图的一人一单的并发性问题,我们使用了基于Redis的分布式锁+lua脚本的方法,但是存在如下问题:

在这里插入图片描述
自己实现上面的功能太麻烦了,可以借用成熟的框架,所以我们要使用Redisson。企业环境下不用自己实现分布式锁,之前的学习都是为了理解原理。

2.Redisson简介

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。
官网地址: https://redisson.org
GitHub地址: https://github.com/redisson/redisson
在这里插入图片描述

3.Redisson入门

1.引入依赖:

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

2.配置Redisson客户端:下面是利用Java的方式来配置(推荐),也可以利用Yaml文件,和SpringBoot整合实现,不推荐使用redisson的starter,会覆盖redis的starter。

@Configuration
public class RedisConfig {

    @Bean
    public RedissonClient redissonClient() {
        // 配置类
        Config config = new Config();
        
        // 添加redis地址,这里添加了单点的地址,也可以使用config.useClusterServer()添加集群地址
        config.useSingleServer()
              .setAddress("redis://192.168.150.101:6379")
              .setPassword("123321");
        
        // 创建客户端
        return Redisson.create(config);
    }
}

3.使用Redisson的分布式锁
在这里插入图片描述

4.Redisson可重入锁的原理

在这里插入图片描述
上图中的锁是无法实现可重入的。上图中左边的代码是同一个线程,但是想要两次获取锁。
那我们如何实现可重入呢?借鉴JDK的Reentrantlock的原理。那Redis如何实现在一个key中存储两个东西(线程ID和重入次数)呢?Hash结构。
在这里插入图片描述
这时候释放锁的逻辑也要改变,要通过次数判断是否为最后的锁(比如value减去一为零了),才会决定是否删除锁,不能不管次数都去释放锁。
在这里插入图片描述
为了保证原子性,我们一定要用lua脚本。

获取锁脚本如下:
在这里插入图片描述
释放锁脚本如下
在这里插入图片描述
代码运行如下:

在这里插入图片描述

boolean isLock =lock.trylock();

找到trylock的实现类,我们使用的最普通的Redissionlock
在这里插入图片描述

@Override
public boolean tryLock(){return get(tryLockAsync());//实现类中存在一个get(tryLockAsync()。

再跟踪
在这里插入图片描述
再跟踪
在这里插入图片描述
在这里插入图片描述
上面的“不管你传没传 leaseTime释放时间,都会进下面蓝色的代码中”,是因为你不传这个参数,会赋值一个默认值,默认值是 -1 ,导致不符合if判断。

再跟踪,发现lua脚本,脚本和我们之前学的是一样的。
在这里插入图片描述
unlock跟踪的话,是一样的,会跟踪到lua脚本。

5.基于Redis的分布式锁优化

我们之前自己的分布式锁,除了不可重入,还不可重试,也就是获取锁一次失败了就停止了。还有超时释放问题:锁超时释放虽然可以避免死锁,但如果是业务执行耗时较长,也会导致业务没有结束,锁就被释放,存在安全隐患。
在这里插入图片描述
我们trylock可以添加三个参数,添加了第一个参数-等待时间,就可以变成可重试的锁了。

Lock.java 接口如下

boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

找到实现,如下
RedissionLock,class:

public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException {
        return this.tryLock(waitTime, -1L, unit);//tryLock代码如下
    }
    
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();  // 获取当前线程的ID,用于锁的订阅
    Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);  // 尝试获取锁并返回锁的剩余时间(ttl)
    if (ttl == null) {  // 如果ttl为null,表示锁获取成功
        return true;  // 返回true,表示获取锁成功
    } else {
        time -= System.currentTimeMillis() - current;  // 计算获取锁所需的时间,并更新剩余等待时间
        if (time <= 0L) {  // 如果剩余等待时间小于等于0,说明已经超时,无法获取锁
            this.acquireFailed(waitTime, unit, threadId);  // 获取锁失败,记录失败信息
            return false;  // 返回false,表示获取锁失败
        } else {
            current = System.currentTimeMillis();  // 重新记录当前时间 这里不能立即尝试获取锁,因为我们刚得知获得锁的进程的业务还没有结束,立刻尝试,大概率还是没结束。
            RFuture<RedissonLockEntry> subscribeFuture = this.subscribe(threadId);  // 订阅锁的获取事件,返回一个Future对象,表示异步操作 与后面的lua脚本中的publish呼应
            if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {  // 如果等待超时,返回false,!false就是true。这里就是一直等待有人释放锁。
                if (!subscribeFuture.cancel(false)) {  //超时了,订阅已经没用了,我就取消订阅 如果取消订阅失败,执行回调
                    subscribeFuture.onComplete((res, e) -> {  // 在订阅完成时执行回调函数
                        if (e == null) {  // 如果没有异常发生,取消订阅
                            this.unsubscribe(subscribeFuture, threadId);
                        }
                    });
                }

                this.acquireFailed(waitTime, unit, threadId);  // 获取锁失败,记录失败信息
                return false;  // 返回false,表示获取锁失败
            } else {
                try {//开始重试
                    time -= System.currentTimeMillis() - current;  // 更新剩余等待时间,严格把控时间
                    if (time <= 0L) {  // 如果剩余等待时间小于等于0,表示已经超时
                        this.acquireFailed(waitTime, unit, threadId);  // 获取锁失败,记录失败信息
                        boolean var20 = false;  // 定义返回值
                        return var20;  // 返回false,表示获取锁失败
                    } else {
                        boolean var16;  // 定义标志变量,用于标识是否成功获取锁
                        do {
                            long currentTime = System.currentTimeMillis();  // 记录当前时间
                            ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);  // 尝试重新获取锁,返回锁的剩余时间
                            if (ttl == null) {  // 如果获取锁成功(ttl为null)
                                var16 = true;  // 设置为true,表示获取锁成功
                                return var16;  // 返回true,表示获取锁成功
                            }

                            time -= System.currentTimeMillis() - currentTime;  // 更新剩余等待时间
                            if (time <= 0L) {  // 如果剩余时间小于等于0,表示已经超时
                                this.acquireFailed(waitTime, unit, threadId);  // 获取锁失败,记录失败信息
                                var16 = false;  // 设置为false,表示获取锁失败
                                return var16;  // 返回false,表示获取锁失败
                            }

                            currentTime = System.currentTimeMillis();  // 记录当前时间
                            if (ttl >= 0L && ttl < time) {  // 也不是立即尝试,要等待一段时间。如果锁的剩余过期时间小于剩余等待时间
                                ((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);  // 尝试获取锁直到ttl,
                            } else {
                                ((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);  // 否则,按剩余时间尝试获取锁
                            }

                            time -= System.currentTimeMillis() - currentTime;  // 更新剩余等待时间
                        } while(time > 0L);  // 如果还有剩余时间,则继续尝试获取锁

                        this.acquireFailed(waitTime, unit, threadId);  // 获取锁失败,记录失败信息
                        var16 = false;  // 设置为false,表示获取锁失败
                        return var16;  // 返回false,表示获取锁失败
                    }
                } finally {
                    this.unsubscribe(subscribeFuture, threadId);  // 无论成功或失败,最后取消订阅
                }
            }
        }
    }
}
<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]);",//返回key的剩余过期时间
                Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
    }
protected RFuture<Boolean> unlockInnerAsync(long threadId) {
        return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                        "return nil;" +
                        "end; " +
                        "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
                        "if (counter > 0) then " +
                        "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                        "return 0; " +
                        "else " +
                        "redis.call('del', KEYS[1]); " +
                        "redis.call('publish', KEYS[2], ARGV[1]); " + //publish 与前面的订阅相关
                        "return 1; " +
                        "end; " +
                        "return nil;",
                Arrays.asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
    }

这样锁的重试就完成了。接下来要解决,所不能因为阻塞而超时释放,要因为我的业务执行完毕而释放。

private RFuture<Boolean> tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
        if (leaseTime != -1) {
            return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
        }
        //如果你指定了leaseTime,那么就走上面的if,用自己的释放时间,如果你没有就走下面看门口的释放时间(private long lockWatchdogTimeout = 30 * 1000;)。
        RFuture<Boolean> ttlRemainingFuture = tryLockInnerAsync(waitTime,
                                                    commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),
                                                    TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
        ttlRemainingFuture.onComplete((ttlRemaining, e) -> {//Future.onComplete就是当future完成以后,有点像回调函数
            if (e != null) {
                return;
            }

            // lock acquired
            if (ttlRemaining) {
                scheduleExpirationRenewal(threadId);//有一个自动更新续期的过程
            }
        });
        return ttlRemainingFuture;
    }

上面这段代码是一个异步方法,它尝试获取一个锁,返回一个RFuture<Boolean>,指示是否成功获取锁。代码的每一行做了以下操作:

private RFuture<Boolean> tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {

这是方法的声明。tryAcquireOnceAsync是一个异步方法,返回一个RFuture<Boolean>,表示锁是否成功获取。方法接收四个参数:

  • waitTime:等待获取锁的最大时间。
  • leaseTime:锁的租期(即持有锁的最大时间)。
  • unit:时间单位(例如秒、毫秒)。
  • threadId:请求锁的线程的ID。
if (leaseTime != -1) {
    return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);
}

这部分判断leaseTime是否等于-1,如果不等于-1,调用tryLockInnerAsync方法来异步尝试获取锁。tryLockInnerAsync方法的参数包括:

  • waitTime:等待的最大时间。
  • leaseTime:锁的租期。
  • unit:时间单位。
  • threadId:请求锁的线程ID。
  • RedisCommands.EVAL_NULL_BOOLEAN:一个标识符,可能用于Redis命令的执行。
RFuture<Boolean> ttlRemainingFuture = tryLockInnerAsync(waitTime,
                                            commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),
                                            TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);

如果leaseTime等于-1,代码继续尝试获取锁,这时它使用一个不同的leaseTime。它通过commandExecutor获取连接管理器的配置,使用getLockWatchdogTimeout()获取一个默认的超时时间,单位是毫秒。然后它调用tryLockInnerAsync异步获取锁,返回一个RFuture<Boolean>,表示锁是否成功获取。

ttlRemainingFuture.onComplete((ttlRemaining, e) -> {

ttlRemainingFuture注册一个回调,回调会在ttlRemainingFuture完成时执行。onComplete方法接收两个参数:

  • ttlRemaining:表示锁是否成功获取的结果。
  • e:可能发生的异常。
if (e != null) {
    return;
}

检查是否有异常发生。如果e不为null,表示发生了异常,此时直接返回,不再执行后续逻辑。

// lock acquired
if (ttlRemaining) {
    scheduleExpirationRenewal(threadId);
}

如果没有异常,且ttlRemainingtrue(表示成功获取锁),则调用scheduleExpirationRenewal(threadId)方法,可能是为锁续期,确保锁在租期内有效。

return ttlRemainingFuture;

最终,返回之前创建的ttlRemainingFuture,该对象表示锁的获取过程是否成功。

private void scheduleExpirationRenewal(long threadId) {
        ExpirationEntry entry = new ExpirationEntry();
        ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);//为每一个锁对应一个entry。
        if (oldEntry != null) {
            oldEntry.addThreadId(threadId);//把线程Id放入旧的,实现可重入。
        } else {
            entry.addThreadId(threadId);//把线程Id放入新的entry,为之后重入做准备。
            renewExpiration();
        }
    }

上面代码中的getEntryName()

public RedissonLock(CommandAsyncExecutor commandExecutor, String name) {
        super(commandExecutor, name);
        this.commandExecutor = commandExecutor;
        this.id = commandExecutor.getConnectionManager().getId();//连接的名字
        this.internalLockLeaseTime = commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout();
        this.entryName = id + ":" + name;
        this.pubSub = commandExecutor.getConnectionManager().getSubscribeService().getLockPubSub();
}

protected String getEntryName() {
   		return entryName;
}
private void renewExpiration() {
        ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
        if (ee == null) {
            return;
        }
        //创建一个新的定时任务(TimerTask),它将在指定的时间间隔后执行。
        //在这里,newTimeout 用来创建一个超时任务。
        //commandExecutor.getConnectionManager() 获取一个连接管理器实例,newTimeout 则是通过该管理器生成超时任务。
        //定时任务的执行代码由匿名类 TimerTask 定义。
        Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
        //在 TimerTask 中,run 方法是定时任务触发时执行的逻辑。
        //此时,超时任务开始执行,参数 timeout 是由 newTimeout 方法传入的。
            @Override
            public void run(Timeout timeout) throws Exception {
                ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
                if (ent == null) {
                    return;
                }
                Long threadId = ent.getFirstThreadId();
                if (threadId == null) {
                    return;
                }
                
                RFuture<Boolean> future = renewExpirationAsync(threadId);//重置锁的有效期
                future.onComplete((res, e) -> {
                    if (e != null) {
                        log.error("Can't update lock " + getName() + " expiration", e);
                        return;
                    }
                    
                    if (res) {
                        // reschedule itself
                        renewExpiration();//调用自己,实现无情物尽,一直执行,锁的有效期一直被重置,永不过期。
                    }
                });
            }
        }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);//internalLockLeaseTime获取的是看门狗的时间。
        
        ee.setTimeout(task);
    }
    

在这里插入图片描述

protected RFuture<Boolean> renewExpirationAsync(long threadId) {
        return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
                "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                        "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                        "return 1; " +
                        "end; " +
                        "return 0;",
                Collections.singletonList(getName()),
                internalLockLeaseTime, getLockName(threadId));
    }

如何取消不断地重置锁的有效期呢?在unlock
在这里插入图片描述
在这里插入图片描述

流程

在这里插入图片描述

总结

Redisson分布式锁原理

  • 可重入:利用hash结构记录线程id和重入次数
  • 可重试:利用信号量和PubSub功能实现等待、唤醒,获取锁失败的重试机制
  • 超时续约:利用watchDog,每隔一段时间(releaseTime/3),重置超时时间

Redission的multiLock原理

Redis如果一个节点坏了,那我们就无法正常运转了,所以我们要有多个Redis节点同时运行,但这样就出现了主从一致性的问题。
在这里插入图片描述还没来得及同步数据,主节点宕机,会选一个新的从节点当主节点。这个新的主节点,会给另外一个应用锁,导致两个线程同时持有一把锁。

Redission如何解决主从一致性问题。

在这里插入图片描述
在这里插入图片描述
就算有一个节点宕机了,一个新的Java应用也无法获取锁,因为还有其他Redis节点保存锁的信息。
把多个独立的锁联合在一起,叫做multiLock。

如下图所示,有多个Redis节点。
在这里插入图片描述
配置redissionClient
在这里插入图片描述
在这里插入图片描述
创建联锁如下:
在这里插入图片描述
此时执行下面的lock代码会发现三个Redis节点都有了一把锁(key不一样),并且都是可重入的,次数相同。
在这里插入图片描述
跟踪代码:这次是RedissionMultiLock,不是上面的RedissionLock

在这里插入图片描述

@Override
    public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
//        try {
//            return tryLockAsync(waitTime, leaseTime, unit).get();
//        } catch (ExecutionException e) {
//            throw new IllegalStateException(e);
//        }
		//在 Redisson 中,锁的 施放时间(leaseTime)和 等待时间(waitTime)的倒计时都与锁的尝试获取(即 tryLock 方法)开始的时刻相关。具体来说,它们的倒计时从调用 tryLock 方法的那一刻开始计算。
		
        long newLeaseTime = -1;
        if (leaseTime != -1) {
            if (waitTime == -1) {
                newLeaseTime = unit.toMillis(leaseTime);
            } else {
                newLeaseTime = unit.toMillis(waitTime)*2;// 如果设置了等待时间,我们要保证施放时间比等待时间长才可以。
            }
        }
        
        long time = System.currentTimeMillis();
        long remainTime = -1;
        if (waitTime != -1) {
            remainTime = unit.toMillis(waitTime);
        }
        long lockWaitTime = calcLockWaitTime(remainTime);
        
        int failedLocksLimit = failedLocksLimit();//获取最大(获取)失败锁的限制 failedLocksLimit
        List<RLock> acquiredLocks = new ArrayList<>(locks.size());//然后创建一个 acquiredLocks 列表来存储成功获取的锁。
        for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {//遍历我们需要获取的锁
            RLock lock = iterator.next();
            boolean lockAcquired;//保存是否成功获取了锁
            try {
                if (waitTime == -1 && leaseTime == -1) {//判断是否可重试得去获取锁
                    lockAcquired = lock.tryLock();
                } else {
                    long awaitTime = Math.min(lockWaitTime, remainTime);
                    lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);
                }
            } catch (RedisResponseTimeoutException e) {
                unlockInner(Arrays.asList(lock));
                lockAcquired = false;
            } catch (Exception e) {
                lockAcquired = false;
            }
            
            if (lockAcquired) {
                acquiredLocks.add(lock);//如果获取锁成功,那就添加lock
            } else {
                if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {//如果需要获取的锁的数量减去获得了的锁的数量等于获取锁失败数量的限制值,那就break,如果限制值为0,也就是说要获得所有的锁,才能推出循环。
                    break;
                }

                if (failedLocksLimit == 0) {
                    unlockInner(acquiredLocks);//把拿到的锁释放掉
                    if (waitTime == -1) {//等于-1,表示不想重试,一次失败就停止代码
                        return false;
                    }
                    //想重试的话,进行下面代码
                    failedLocksLimit = failedLocksLimit();
                    acquiredLocks.clear();
                    // reset iterator
                    while (iterator.hasPrevious()) {//把迭代器回退到最前面,从新开始获取所有锁。
                        iterator.previous();
                    }
                } else {//如果 failedLocksLimit 大于 0,减少 failedLocksLimit 并继续尝试获取剩下的锁。
                    failedLocksLimit--;
                }
            }
            
            if (remainTime != -1) {
                remainTime -= System.currentTimeMillis() - time;
                time = System.currentTimeMillis();
                if (remainTime <= 0) {//如果剩余等待时间不够了,我们还有锁没有获得,所以获取所有锁的任务失败,我们要返回获取锁失败(之前我们要释放已经获取的锁)。
                    unlockInner(acquiredLocks);
                    return false;
                }
            }
        }

        if (leaseTime != -1) {//如果设置了施放时间,那我们要在获取完所有锁后,都重置一下施放时间,要不然最开始获取的锁的释放时间会小于最后获取的锁。
            List<RFuture<Boolean>> futures = new ArrayList<>(acquiredLocks.size());
            for (RLock rLock : acquiredLocks) {
                RFuture<Boolean> future = ((RedissonLock) rLock).expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);
                futures.add(future);
            }
            
            for (RFuture<Boolean> rFuture : futures) {
                rFuture.syncUninterruptibly();
            }
        }
        
        return true;
    }

总结Redis分布式锁

在这里插入图片描述

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐