一、加锁流程

1. 核心方法调用链
RLock lock = redisson.getLock("resource");
lock.lock(); // 阻塞式加锁↳ lockInterruptibly()↳ tryAcquire(-1, leaseTime, unit) // leaseTime=-1表示启用看门狗↳ tryAcquireAsync()↳ tryLockInnerAsync() // 执行Lua脚本
2. Lua脚本实现(关键)
// RedissonLock.tryLockInnerAsync()
"if (redis.call('exists', KEYS[1]) == 0) then " +  // 锁不存在"redis.call('hset', KEYS[1], ARGV[2], 1); " +  // 创建锁(Hash结构)"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); " +  // 重入次数+1"redis.call('pexpire', KEYS[1], ARGV[1]); " +  // 重置过期时间"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",  // 返回剩余时间,加锁失败
Collections.singletonList(getName()),  // KEYS[1]: 锁名称(如"resource")
internalLockLeaseTime, getLockName(threadId)  // ARGV[1]: 过期时间;ARGV[2]: 线程标识(UUID:threadId)
3. 关键点
  • 原子性:通过Lua脚本保证检查锁和创建锁的原子性。
  • 数据结构:使用Redis的Hash存储锁信息,field为线程标识,value为重入次数。
  • 过期时间:默认30秒(看门狗机制自动续期),防止死锁。

二、解锁流程

1. 核心方法调用链
lock.unlock();↳ unlockAsync()↳ unlockInnerAsync() // 执行Lua脚本
2. Lua脚本实现(关键)
// RedissonLock.unlockInnerAsync()
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +  // 锁不存在或非当前线程持有"return nil; " +
"end; " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +  // 重入次数-1
"if (counter > 0) then " +  // 重入次数>0,继续持有锁"redis.call('pexpire', KEYS[1], ARGV[2]); " +  // 重置过期时间"return 0; " +
"else " +  // 重入次数=0,释放锁"redis.call('del', KEYS[1]); " +  // 删除锁"redis.call('publish', KEYS[2], ARGV[1]); " +  // 发布锁释放消息(通知等待线程)"return 1; " +
"end; " +
"return nil;",
Arrays.asList(getName(), getChannelName()),  // KEYS[1]: 锁名称;KEYS[2]: 发布订阅通道
LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId)  // ARGV[3]: 线程标识
3. 关键点
  • 安全释放:仅锁持有者(UUID:threadId匹配)可释放锁。
  • 发布订阅:锁释放时通过Redis的PUBLISH通知等待线程。
  • 重入处理:通过hincrby -1递减重入次数,确保正确释放。

三、锁续时(看门狗机制)

1. 触发条件
  • 当使用无参lock()方法时(即未指定leaseTime),默认启用看门狗。
  • 看门狗默认每10秒(internalLockLeaseTime / 3)续期一次,将锁过期时间重置为30秒。
2. 核心源码
// RedissonLock.scheduleExpirationRenewal()
private void scheduleExpirationRenewal(long threadId) {ExpirationEntry entry = new ExpirationEntry();ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);entry.addThreadId(threadId);// 创建定时任务Timeout task = commandExecutor.getConnectionManager().newTimeout(timeout -> {RFuture<Boolean> future = renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) {log.error("Can't update lock " + getName() + " expiration", e);return;}if (res) {// 续期成功,递归调用scheduleExpirationRenewal(threadId);}});}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);entry.setTimeout(task);
}
3. 续期Lua脚本
// RedissonLock.renewExpirationAsync()
"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)
4. 关键点
  • 自动续期:通过Netty的Timeout实现定时任务。
  • 避免死锁:若业务执行时间超长,看门狗会持续续期,直到业务完成或线程崩溃。

四、重入锁实现

1. 数据结构

使用Redis的Hash存储锁信息:

  • Key:锁名称(如"resource")。
  • Field:线程标识(UUID:threadId)。
  • Value:重入次数(初始为1,每次重入+1)。
2. 加锁时的重入逻辑
// Lua脚本片段
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +  // 锁已存在,判断是否重入"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +  // 重入次数+1"redis.call('pexpire', KEYS[1], ARGV[1]); " +  // 重置过期时间"return nil; " +  // 返回nil表示加锁成功(重入)
"end; "
3. 解锁时的重入逻辑
// Lua脚本片段
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +  // 重入次数-1
"if (counter > 0) then " +  // 重入次数>0,继续持有锁"redis.call('pexpire', KEYS[1], ARGV[2]); " +  // 重置过期时间"return 0; " +  // 返回0表示锁未释放
"else " +  // 重入次数=0,释放锁"redis.call('del', KEYS[1]); " +  // 删除锁"return 1; " +  // 返回1表示锁已释放
"end; "
4. 关键点
  • 线程安全:通过UUID:threadId确保同一线程可重入。
  • 原子计数:使用hincrby保证计数操作的原子性。

五、lock()与tryLock()的区别

1. 核心区别对比表

特性

lock()

tryLock()

阻塞行为

阻塞直到获取锁

立即返回或在指定时间内等待

超时机制

无超时,默认启用看门狗自动续期

可自定义等待时间和锁持有时间

异常处理

不响应中断(抛出 InterruptedException

可响应中断(通过重载方法)

返回值

void

boolean(是否获取锁成功)

看门狗默认启用

是(无参时)

否(需显式设置超时参数)

典型场景

必须获取锁才能执行的场景

可重试或放弃的场景

2. 源码差异分析
// lock() 源码片段
public void lock() {try {lock(-1, null, false); // leaseTime=-1表示启用看门狗} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}// tryLock() 源码片段
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {// 1. 计算超时时间long time = unit.toMillis(waitTime);long current = System.currentTimeMillis();long threadId = Thread.currentThread().getId();// 2. 尝试获取锁Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);if (ttl == null) {return true; // 获取成功}// 3. 超时处理逻辑(循环尝试或等待通知)// ...
}
3. 使用场景对比
// lock() 使用示例
RLock lock = redisson.getLock("order:123");
try {lock.lock(); // 阻塞直到获取锁// 执行关键业务逻辑
} finally {lock.unlock();
}// tryLock() 使用示例
RLock lock = redisson.getLock("inventory:apple");
try {// 尝试在5秒内获取锁,持有30秒if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {// 获取锁成功,执行操作} else {// 获取锁失败,执行降级逻辑}
} catch (InterruptedException e) {Thread.currentThread().interrupt();
} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}
}

六、总结

Redisson分布式锁的核心优势:

  1. 原子性:通过Lua脚本确保操作的原子性。
  2. 可重入:基于Hash结构实现线程级别的重入计数。
  3. 高可用:通过看门狗机制避免锁过期导致的数据不一致。
  4. 高性能:基于Netty的异步通信模型。
  5. 安全释放:通过UUID:threadId确保锁只能被持有者释放。

最佳实践建议

  • 优先使用 tryLock():避免长时间阻塞,提高系统吞吐量。
  • 明确锁持有时间:根据业务场景合理设置leaseTime,避免过度依赖看门狗。
  • 异常处理:使用带超时参数的tryLock(),并处理中断异常。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/908219.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/908219.shtml
英文地址,请注明出处:http://en.pswp.cn/news/908219.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于React + TypeScript构建高度可定制的QR码生成器

前言 在现代Web应用中&#xff0c;QR码已成为连接线上线下的重要桥梁。本文将详细介绍如何使用React TypeScript Vite构建一个功能强大、高度可定制的QR码生成器&#xff0c;支持背景图片、文本叠加、HTML模块、圆角导出等高级功能。 前往试试 项目概述 技术栈 前端框架:…

【MATLAB代码】制导——三点法,二维平面下的例程|运动目标制导,附完整源代码

三点法制导是一种导弹制导策略,主要用于确保导弹能够准确追踪并击中移动目标。该方法通过计算导弹、目标和制导站之间的相对位置关系,实现对目标的有效制导。 本文给出MATLAB下的三点法例程,模拟平面上捕获运动目标的情况订阅专栏后可直接查看源代码,粘贴到MATLAB空脚本中即…

Ubuntu22.04 安装 IsaacSim 4.2.0

1. 从官网下载 IsaacSim 4.2.0 安装包 https://download.isaacsim.omniverse.nvidia.com/isaac-sim-standalone%404.2.0-rc.18%2Brelease.16044.3b2ed111.gl.linux-x86_64.release.zip 2. 查阅 Workstation Installation 安装方式 Workstation Installation — Isaac Sim Do…

开源量子模拟引擎:Quantum ESPRESSO本地部署教程,第一性原理计算轻松入门!

一、介绍 Quantum ESPRESSO 是一个用于电子结构计算和纳米尺度材料建模的开源计算机代码集成套件&#xff0c;专门用于进行第一性原理&#xff08;第一性原理&#xff09;计算&#xff0c;涵盖了电子结构、晶体学和材料性能的模拟。 Quantum ESPRESSO GPU 版本支持GPU加速&am…

PVE 虚拟机安装 Ubuntu Server V24 系统 —— 一步一步安装配置基于 Ubuntu Server 的 NodeJS 服务器详细实录1

前言 最近在基于 NodeJS V22 写一个全栈的项目&#xff0c;写好了&#xff0c;当然需要配置服务器部署啦。这个过程对于熟手来说&#xff0c;还是不复杂的&#xff0c;但是对于很多新手来说&#xff0c;可能稍微有点困难。所以&#xff0c;我把整个过程全部记录一下。 熟悉我…

【JUC】深入解析 JUC 并发编程:单例模式、懒汉模式、饿汉模式、及懒汉模式线程安全问题解析和使用 volatile 解决内存可见性问题与指令重排序问题

单例模式 单例模式确保某个类在程序中只有一个实例&#xff0c;避免多次创建实例&#xff08;禁止多次使用new&#xff09;。 要实现这一点&#xff0c;关键在于将类的所有构造方法声明为private。 这样&#xff0c;在类外部无法直接访问构造方法&#xff0c;new操作会在编译…

2. 库的操作

2.1 创建数据库 语法&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ...] create_specification: [DEFAULT] CHARACTER SET charset_name # 字符集: 存储编码 [DEFAULT] COLLATE collation_name # 校验集: 比较/选择/读…

道可云人工智能每日资讯|北京农业人工智能与机器人研究院揭牌

道可云人工智能&元宇宙每日简报&#xff08;2025年6月3日&#xff09;讯&#xff0c;今日人工智能&元宇宙新鲜事有&#xff1a; 北京农业人工智能与机器人研究院揭牌 5月30日&#xff0c;北京市农业农村局、北京市海淀区人民政府、北京市农林科学院共同主办北京农业人…

【JSON-to-Video】设置背景视频片断

目录 设置bgVideo字段 1. 设置bgVideo.videoList字段 2. 设置randomPlay字段 3. 设置complete字段 4. 调用API&#xff0c;制作视频 欢迎来到JSON转视频系列教程。今天要教大家如何添加背景视频片断&#xff0c;在视频制作中&#xff0c;巧妙运用背景视频&#xff0c;能为…

星闪开发之Server-Client 指令交互控制红灯亮灭案例解析(SLE_LED详解)

系列文章目录 星闪开发之Server-Client 指令交互控制红灯亮灭的全流程解析&#xff08;SLE_LED详解&#xff09; 文章目录 系列文章目录前言一、项目地址二、客户端1.SLE_LED_Client\inc\SLE_LED_Client.h2.SLE_LED_Client\src\SLE_LED_Client.c头文件与依赖管理宏定义与全局变…

Linux shell练习题

Shell 1. 判断~/bigdata.txt 是否存在&#xff0c;若已存在则打印出”该文件已存在“&#xff0c;如不存在&#xff0c;则输出打印&#xff1a;”该文件不存在“ if [ -f ./bigdata.txt ];then echo "文件存在" else echo "文件不存在" fi2. 判断~/bigd…

Linux基本指令(三)

接上之前的文章&#xff0c;咱继续分享Linux的基本指令&#xff0c;Linux指令比较多&#xff0c;很难全部记住需要做笔记对常用的指令进行记录&#xff0c;方便以后复习查找&#xff0c;做笔记也可以对知识理解更加深刻。 目录 时间相关指令 date显示 时间戳 cal指令 ​编…

WebRTC中sdp多媒体会话协议报文详细解读

sdp介绍 在WebRTC&#xff08;Web实时通信&#xff09;中&#xff0c;SDP&#xff08;Session Description Protocol&#xff09;是用来描述和协商多媒体会话的协议。它定义了会话的参数和媒体流的信息&#xff0c;如音视频编码格式、传输方式、网络地址等。SDP是WebRTC中一个…

【MySQL】 约束

一、约束的定义 MySQL 约束是用于限制表中数据的规则&#xff0c;确保数据的 准确性 和 一致性 。约束可以在创建表时定义&#xff0c;也可以在表创建后通过修改表结构添加。 二、常见的约束类型 2.1 NOT NULL 非空约束 加了非空约束的列不能为 NULL 值&#xff0c;如果可以…

【.net core】【watercloud】树形组件combotree导入及调用

源码下载:combotree: 基于layui及zTree的树下拉框组件 链接中提供了组件的基本使用方法 框架修改内容 1.文件导入&#xff08;路径可更具自身情况自行设定&#xff09; 解压后将文件夹放在图示路径下&#xff0c;修改文件夹名称为combotree 2.设置路径&#xff08;设置layu…

ES101系列07 | 分布式系统和分页

本篇文章主要讲解 ElasticSearch 中分布式系统的概念&#xff0c;包括节点、分片和并发控制等&#xff0c;同时还会提到分页遍历和深度遍历问题的解决方案。 节点 节点是一个 ElasticSearch 示例 其本质就是一个 Java 进程一个机器上可以运行多个示例但生产环境推荐只运行一个…

CppCon 2015 学习:3D Face Tracking and Reconstruction using Modern C++

1. 3D面部追踪和重建是什么&#xff1f; 3D面部追踪&#xff08;3D Face Tracking&#xff09;&#xff1a; 实时检测并追踪人脸在三维空间中的位置和姿态&#xff08;如转头、点头、表情变化等&#xff09;&#xff0c;通常基于摄像头捕获的视频帧。3D面部重建&#xff08;3D…

代码中的问题及解决方法

目录 YOLOX1. AttributeError: VOCDetection object has no attribute cache2. ValueError: operands could not be broadcast together with shapes (8,5) (0,)3. windows远程查看服务器的tensorboard4. AttributeError: int object has no attribute numel YOLOX 1. Attribu…

【JVM】Java类加载机制

【JVM】Java类加载机制 什么是类加载&#xff1f; 在 Java 的世界里&#xff0c;每一个类或接口在经过编译后&#xff0c;都会生成对应的 .class 字节码文件。 所谓类加载机制&#xff0c;就是 JVM 将这些 .class 文件中的二进制数据加载到内存中&#xff0c;并对其进行校验…

vue的监听属性watch的详解

文章目录 1. 监听属性 watch2. 常规用法3. 监听对象和route变化4. 使用场景 1. 监听属性 watch watch 是一个对象&#xff0c;键是需要观察的表达式&#xff0c;用于观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数的参数是新值和旧值。值也可以是方法名&am…