redis-redission的加锁源码与看门狗机制

embedded/2025/1/23 1:28:36/

redission加锁方式

maven依赖

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

 

lock使用方式

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;import java.util.concurrent.TimeUnit;public class RedissionLock {private static final String KEY = "lock_test";private static final RedissonClient redisson;static {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.0:6379").setUsername("***").setPassword("****");redisson = Redisson.create(config);}public static void main(String[] args) {RLock lock = redisson.getLock(KEY);try {lock.lock();// 该方式不会启动看门狗lock.lock(20, TimeUnit.SECONDS);} finally {lock.unlock();}}}

内部方法调用顺序

调用lock

    public void lock() {try {this.lock(-1L, (TimeUnit)null, false);} catch (InterruptedException var2) {throw new IllegalStateException();}}

   private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {long threadId = Thread.currentThread().getId();// 加锁成功ttl为nullLong ttl = this.tryAcquire(-1L, leaseTime, unit, threadId);// 加锁不成功,会自旋尝试重新加锁if (ttl != null) {// ......}}

 lock(long leaseTime, TimeUnit unit, boolean interruptibly)方法的含义

leaseTime

含义:锁的持有时间,即锁的自动释放时间。单位由TimeUnit参数指定。

作用:如果leaseTime-1,表示锁不会自动释放,需要手动调用unlock()方法释放锁。

unit

含义:leaseTime的时间单位,例如TimeUnit.SECONDSTimeUnit.MILLISECONDS

作用:指定leaseTime的时间单位,确保锁的持有时间被正确解析。

interruptibly

含义:是否响应线程中断。

作用:如果为false,表示在尝试获取锁的过程中,线程不会响应中断,即使被中断也会继续尝试获取锁。如果为true,表示在尝试获取锁的过程中,如果当前线程被中断(调用了Thread.interrupt()),会抛出InterruptedException,并停止尝试获取锁。

 

tryAcquire

    private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {return (Long)this.get(this.tryAcquireAsync(waitTime, leaseTime, unit, threadId));}

 

waitTime

含义:尝试获取锁的最大等待时间。

单位:由 TimeUnit 参数指定(如秒、毫秒等)。

作用:指定当前线程尝试获取锁的最大等待时间。如果在该时间内无法获取锁,则返回 false

leaseTime

类型:long

含义:锁的持有时间。

单位:由 TimeUnit 参数指定(如秒、毫秒等)。

作用:指定锁的自动释放时间。如果锁在 leaseTime 时间内未被显式释放,Redisson 会自动释放该锁。这可以防止锁被永久持有,从而避免死锁问题。

unit

含义:时间单位,用于指定 waitTimeleaseTime 的时间单位。

作用:确保时间参数的单位一致性。常见的值包括:

threadId

含义:当前线程的唯一标识符。

作用:用于标识当前线程,以便 Redisson 能够跟踪锁的持有者。在分布式环境中,每个线程的 threadId 是唯一的。

返回值

返回值含义

如果成功获取锁,返回 1

如果在指定的 waitTime 内未能获取锁,返回 null0

tryAcquireAsync

    private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {RFuture<Long> ttlRemainingFuture;if (leaseTime != -1L) {ttlRemainingFuture = this.<Long>tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else {// 持有锁的时间为-1时,会设置默认的持有持有时间30秒ttlRemainingFuture = this.<Long>tryLockInnerAsync(waitTime, this.internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);}// ttlRemaining 不为null,则表示锁已经被抢占,该值为剩余时间CompletionStage<Long> f = ttlRemainingFuture.thenApply((ttlRemaining) -> {if (ttlRemaining == null) {if (leaseTime != -1L) {this.internalLockLeaseTime = unit.toMillis(leaseTime);} else {// ttlRemaining 为null,表示加锁成功;// 没有设置leaseTime,则触发看门狗进行自动续期//(如果设置了,则不会触发看门狗)this.scheduleExpirationRenewal(threadId);}}return ttlRemaining;});return new CompletableFutureWrapper(f);}

 返回值

返回值类型RFuture<Long>

返回值含义

如果锁被成功获取,返回值为 null

如果锁未被获取,返回值为锁的剩余过期时间(单位为毫秒)

tryLockInnerAsync

    <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {return this.evalWriteAsync(
this.getRawName(), 
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(this.getRawName()),new Object[]{unit.toMillis(leaseTime), 
this.getLockName(threadId)});
}

加锁,使用hash结构

看门狗

scheduleExpirationRenewal 

添加续期任务

    protected void scheduleExpirationRenewal(long threadId) {ExpirationEntry entry = new ExpirationEntry();ExpirationEntry oldEntry = (ExpirationEntry)EXPIRATION_RENEWAL_MAP.putIfAbsent(this.getEntryName(), entry);if (oldEntry != null) {oldEntry.addThreadId(threadId);} else {entry.addThreadId(threadId);try {// 续期this.renewExpiration();} finally {// 线程被打断,终止需求(避免线程意外死亡,锁被一致续期的情况)   if (Thread.currentThread().isInterrupted()) {this.cancelExpirationRenewal(threadId);}}}}

    private void renewExpiration() {ExpirationEntry ee = (ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());if (ee != null) {Timeout task = this.commandExecutor.getConnectionManager().newTimeout(
// 创建定时任务(定时任务的执行间隔为this.internalLockLeaseTime/3 )
new TimerTask() {public void run(Timeout timeout) throws Exception {ExpirationEntry ent = (ExpirationEntry)RedissonBaseLock.EXPIRATION_RENEWAL_MAP.get(RedissonBaseLock.this.getEntryName());if (ent != null) {Long threadId = ent.getFirstThreadId();if (threadId != null) {// 续期RFuture<Boolean> future = RedissonBaseLock.this.renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) {RedissonBaseLock.log.error("Can't update lock " + RedissonBaseLock.this.getRawName() + " expiration", e);RedissonBaseLock.EXPIRATION_RENEWAL_MAP.remove(RedissonBaseLock.this.getEntryName());} else {if (res) {// 续期成功,继续续期RedissonBaseLock.this.renewExpiration();} else {// 续期失败,取消续期RedissonBaseLock.this.cancelExpirationRenewal((Long)null);}}});}}}}, 
// 在internalLockLeaseTime/3的时间间隔,发起续租(默认30秒,即10秒续租一次)
this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);ee.setTimeout(task);}}
renewExpirationAsync续期
    protected RFuture<Boolean> renewExpirationAsync(long threadId) {return this.<Boolean>evalWriteAsync(this.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(this.getRawName()), this.internalLockLeaseTime, this.getLockName(threadId));}

lua含义:当key存在时,即设置超时时间

如何保证自动续期
  • 创建一个定时任务,使用定时任务管理器触发任务,进行续期
如何保证任务中断后,看门狗不会自动续期
  • 当任务线程被中断后,会取消看门狗任务(从map中删除任务)
  • 当续期失败后,会取消看门狗任务

建议

业务中尽量避免使用看门狗,评估业务耗时,使用自定义过期时间。因为看门狗太多时会消耗系统资源。


http://www.ppmy.cn/embedded/156203.html

相关文章

如何使用Python脚本将本地项目上传到 GitHub

前言 这里我们通过创建一个新的github仓库&#xff0c;来测试我们的脚本能否上传我们本地的项目&#xff0c;并且进行更新。首先你需要先安装 Git&#xff0c;关于这部分我好像没有记录过&#xff0c;这里我搜索看了一下&#xff0c;这篇博客写的Git安装详解应该是比较齐全的&…

macOS查看当前项目的 tree 结构

文章目录 使用 tree 命令 macOS 系统默认不包含 tree 命令 使用 tree 命令 使用homebrew自动安装脚本/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"安装 tree&#xff1a;brew install tree查看项目的 tree 结构&#…

实现星海波动粒子特效:基于 Canvas 和 JavaScript 的 3D 波动效果

1,前言 近年来,Web 动效和图形呈现技术的不断进步,使得许多动态效果可以通过浏览器轻松呈现。在这篇文章中,我将介绍如何实现一个美丽的 “星海波动” 3D 粒子特效,利用 Canvas 和 JavaScript 绘制出一个带有波动效果的粒子阵列。此特效呈现的是一个平面波的运动,粒子沿…

如何通过云计算优化网站性能?

随着互联网的迅猛发展&#xff0c;网站的性能已经成为用户体验的关键因素之一。响应速度慢、加载时间长&#xff0c;甚至服务器崩溃都会直接影响用户的满意度&#xff0c;进而影响企业的品牌形象和盈利能力。而云计算提供了一个高效、灵活、可扩展的解决方案&#xff0c;帮助企…

html转义符+h5提供的新标签

html转义符 h5提供的新标签 HTML5是HTML从传统的web端开始兼容移动互联网的重要标志&#xff0c;h5为HTML提供了大量好用的标签&#xff0c;如布局使用的三个标签header、section、footer标签&#xff1b;用来播放视频和音频的多媒体标签video、audio标签等&#xff0c;参考表…

2025年国产化推进.NET跨平台应用框架推荐

2025年国产化推进.NET跨平台应用框架推荐 1. .NET MAUI NET MAUI是一个开源、免费&#xff08;MIT License&#xff09;的跨平台框架&#xff08;支持Android、iOS、macOS 和 Windows多平台运行&#xff09;&#xff0c;是 Xamarin.Forms 的进化版&#xff0c;从移动场景扩展到…

什么是报文的大端和小端,有没有什么记忆口诀?

在计算机科学中&#xff0c;**大端&#xff08;Big-Endian&#xff09;和小端&#xff08;Little-Endian&#xff09;**是两种不同的字节序&#xff08;即多字节数据在内存中的存储顺序&#xff09;。理解这两种字节序对于网络通信、文件格式解析以及跨平台编程等非常重要。 1…

EasyExcel的应用

一、简单使用 引入依赖&#xff1a; 这里我们可以使用最新的4.0.2版本&#xff0c;也可以选择之前的稳定版本&#xff0c;3.1.x以后的版本API大致相同&#xff0c;新的版本也会向前兼容&#xff08;3.1.x之前的版本&#xff0c;部分API可能在高版本被废弃&#xff09;&…