Redisson分布式锁-源码分析

news/2024/11/30 5:34:38/

Redisson分布式锁整体流程图

1677219798772.jpg

Redisson分布式锁源码流程图

Redisson分布式锁源码解析

获取分布式锁lock

private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {//获取当前线程IDlong threadId = Thread.currentThread().getId();/*** 尝试获取分布式锁*   a.如果获取到锁:返回null*   b.如果没有获取到锁:返回当前分布式锁的剩余的过期时间*/Long ttl = tryAcquire(-1, leaseTime, unit, threadId);// lock acquiredif (ttl == null) {return;}//ttl不为null说明锁被其他线程占用,没有获取到锁。订阅解锁消息CompletableFuture<RedissonLockEntry> future = subscribe(threadId);pubSub.timeout(future);RedissonLockEntry entry;//订阅解锁消息:如果分布式锁未进行解锁(pub解锁消息),当前线程进入阻塞状态。当接收到分布式锁的pub消息,当前线程被唤醒继续执行if (interruptibly) {entry = commandExecutor.getInterrupted(future);} else {entry = commandExecutor.get(future);}try {while (true) {//当接收到分布式锁的pub消息,当前线程被唤醒继续执行,继续尝试获取锁ttl = tryAcquire(-1, leaseTime, unit, threadId);// lock acquired//获取成功跳出循环if (ttl == null) {break;}// waiting for messageif (ttl >= 0) {try {//阻塞ttl毫秒后继续执行entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} catch (InterruptedException e) {//线程被打断,直接跳出循环if (interruptibly) {throw e;}//线程被打断,但是没有设置打断跳出循环,则继续阻塞ttl毫秒后继续执行entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);}} else {if (interruptibly) {entry.getLatch().acquire();} else {entry.getLatch().acquireUninterruptibly();}}}} finally {unsubscribe(entry, threadId);}//        get(lockAsync(leaseTime, unit));
}

详细步骤如下:

  1. 获取当前获取锁的线程ID。
  2. 调用tryAcquire方法尝试获取锁。
    1. tryAcquire方法返回null,说明获取到了锁。
    2. tryAcquire方法返回不是null值,说明没有获取到了锁,返回的Long值指的是其他线程占用该分布式锁的过期时间,单位为毫秒。
  3. 未获取到锁的线程订阅(sub)解锁(pub)消息,如果分布式锁未进行解锁(pub解锁消息),当前线程进入阻塞状态。当接收到分布式锁的pub消息,当前线程被唤醒继续执行,进入while循环调用tryAcquire方法继续争抢分布式锁。
  4. while中:
    1. 抢到了分布式锁,则跳出循环,并执行finally语句块的取消订阅解锁消息
    2. 如果没有抢到分布式锁,则执行 entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);方法阻塞分布式锁剩余时间ttl毫秒后,继续while循环争取分布式锁,直到抢到分布式锁。

尝试获取分布式锁tryAcquire

尝试获取分布式锁,获取到分布式锁后,开启一个开门狗,为分布式锁续期。为获取到分布式锁,返回分布式锁剩余的过期时间,毫秒为单位。

private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {RFuture<Long> ttlRemainingFuture;//获取分布式锁//返回null则说明获取到了分布式锁//返回不为null说明没有获取到分布式锁,返回的是分布式锁的剩余失效时间if (leaseTime > 0) {ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else {ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);}//解锁的CompletionStage<Long> s = handleNoSync(threadId, ttlRemainingFuture);ttlRemainingFuture = new CompletableFutureWrapper<>(s);//如果获取到分布式锁,则使用看门狗进行锁的续期操作。默认过期时间是30秒,看门狗续期的时间间隔是过期时间的三分之一。CompletionStage<Long> f = ttlRemainingFuture.thenApply(ttlRemaining -> {// lock acquiredif (ttlRemaining == null) {if (leaseTime > 0) {internalLockLeaseTime = unit.toMillis(leaseTime);} else {scheduleExpirationRenewal(threadId);}}return ttlRemaining;});return new CompletableFutureWrapper<>(f);
}

详细步骤如下:

  1. 调用tryLockInnerAsync方法执行Lua脚本获取锁,获取失败返回锁的过期时间。获取成功返回null
  2. 获取分布式锁成功开启一个开门狗,进行锁的续期操作。默认过期时间是30秒,看门狗续期的时间间隔是过期时间的三分之一。
  3. 未获取到锁则返回锁的过期时间,单位毫秒。

获取分布式锁的lua脚本

/*** 执行Lua脚本获取锁* 1.key是否存在* 2.重入锁+1* 3.重置锁的过期时间(默认30秒)*/
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,"if ((redis.call('exists', KEYS[1]) == 0) " +"or (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(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
}

详细步骤如下

  1. key是否存在
  2. 重入锁+1
  3. 重置锁的过期时间(默认30秒)

分布式锁续期

private void renewExpiration() {ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ee == null) {return;}Timeout task = commandExecutor.getServiceManager().newTimeout(new TimerTask() {@Overridepublic 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;}//执行分布式锁续期的lua脚本CompletionStage<Boolean> future = renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) {log.error("Can't update lock {} expiration", getRawName(), e);EXPIRATION_RENEWAL_MAP.remove(getEntryName());return;}if (res) {// reschedule itself//自动续期renewExpiration();} else {//取消分布式锁续期cancelExpirationRenewal(null);}});}}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);ee.setTimeout(task);
}

分布式锁续期lua脚本

protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {return evalWriteAsync(getRawName(), 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(getRawName()),internalLockLeaseTime, getLockName(threadId));
}

http://www.ppmy.cn/news/587171.html

相关文章

Python语法允许在类中再定义类,这被称为嵌套类(nested class)或内部类(inner class)

Python语法允许在类中再定义类&#xff0c;这被称为嵌套类&#xff08;nested class&#xff09;或内部类&#xff08;inner class&#xff09;。在Python中&#xff0c;类可以被视为对象&#xff0c;因此可以在类的定义内部创建其他类。 嵌套类的定义发生在外部类的作用域内&…

老司机手把手教php,老司机手把手教你玩驱魔!纯小白无脑驱魔攻略

纯小白无脑驱魔攻略&#xff0c;从升级路上加点&#xff0c;装备选择&#xff0c;斧头还是念珠&#xff0c;做什么异界&#xff0c;想打安图恩怎么办等一系列问题&#xff0c;统统可以在这得到解答。 首先从驱魔的攻击机制说起&#xff0c;众所周知曾经是分为力驱和法驱的&…

Java又双叒叕“凉”了?

前几天&#xff0c;TIOBE的一份6月编程语言榜单公布&#xff1a;Java退出前三&#xff0c;位居第四。一波Java凉了的言论甚嚣尘上。其实不止Java&#xff0c;python、C、C&#xff0c;哪一个没被提过“凉”... 而现实是&#xff0c;Java的招聘需求依然很大&#xff1a; 不可否…

SOLIDWORKS仿真数据清扫工具

我们来聊下SOLIDWORKS仿真数据清扫工具。与 SOLIDWORKS 软件一起安装的一个鲜为人知的工具是 Simulation Cleaning Utility。该实用工具可用于在 SOLIDWORKS 零件或装配文件中永远删除任何仿真数据&#xff0c;包括仿真设置和后处理信息。 SOLIDWORKS仿真数据清扫工具工具可以…

【网络编程】网络基础(一)

文章目录 一、计算机网络背景1.网络发展2.认识 "协议" 二、网络协议初识1.协议分层2.OSI七层模型3.TCP/IP五层(或四层)模型 三、网络传输基本流程1.网络传输流程图2.数据包首部&#xff08;报头&#xff09;3.数据包封装和分用封装分用 4. 跨局域网主机通信 四、网络…

笔记本CPU利用率卡在16%不动弹的一点解决心得

有时候我的笔记本电脑开机后巨卡顿&#xff0c;开啥都慢&#xff0c;打开任务管理器一看CPU利用率卡在16%上不动弹。网上搜一圈发现没几个人提过这事儿&#xff0c;今天说下我常用的解决方法&#xff0c;也算给遇到相同难题的人留点经验吧。 仅就我这种情况而言&#xff0c;是因…

电脑内存爆满,使用率超过90%

今天突然发现电脑内存爆满&#xff0c;使用率超过97%&#xff0c;但是并没有发现占用内存高的软件&#xff0c;后来网上搜索发现了一个可能解决方法&#xff1a; 更新驱动 用360驱动大师更新完后&#xff0c;内存恢复正常&#xff0c;下降到14%。 大概10天后&#xff1a; 后…

CPU使用率低 内存使用率高

电脑时不时的卡死&#xff0c;尤其是你有点什么事情想做的时候&#xff0c;真的是要疯了。 打开资源管理器一看&#xff0c;CPU使用率只有百分之十几&#xff0c;物理内存使用率却高达九十几&#xff01;这是啥原因呢&#xff1f;之前还真没想过这个问题。现在整理一下&#xf…