在黑马点评项目实战中,提到了可重入锁,然后我想到了是不是不同业务在同一线程内反复获取同一把锁。本文来讨论一下为什么锁需要可重入。

一、可重入锁的核心:“同一线程多次获取同一把锁”

​可重入(Reentrant)​​ 的字面意思是“允许重新进入”,在锁的上下文中,特指​​同一线程可以多次获取同一把锁而不会被阻塞​​。这与“不同业务”无直接关联,而是针对​​同一线程内的嵌套调用或递归调用​​场景设计的。

1. 可重入的典型场景:同一线程的嵌套调用

假设你有一个递归方法或嵌套调用的业务逻辑,例如:

public void methodA() {lock.lock(); // 第一次加锁try {// 业务逻辑1...methodB(); // 调用methodB} finally {lock.unlock(); // 最终释放锁(需确保只释放一次)}
}public void methodB() {lock.lock(); // 嵌套调用,需要再次加锁try {// 业务逻辑2...} finally {lock.unlock();}
}

如果锁是​​不可重入​​的,当methodA获取锁后调用methodB时,methodB尝试再次加锁会被阻塞(因为锁已被当前线程持有),导致死锁。而​​可重入锁​​允许同一线程多次加锁(每次加锁重入次数+1),直到所有加锁操作都被释放(重入次数减至0),从而避免死锁。

2. 与“不同业务”的区别

这里的“不同业务”并非指不同线程或不同功能模块,而是​​同一线程内的不同代码路径​​(如递归、循环调用)。例如:

  • 电商下单场景中,主线程先校验库存(调用checkStock()),再扣减库存(调用deductStock()),两个方法都需要同一把锁。若锁不可重入,checkStock()加锁后,deductStock()会因无法获取锁而阻塞;可重入锁则允许checkStock()加锁后,deductStock()直接获取已持有的锁(重入次数+1)。

二、为什么需要可重入锁?应对复杂业务的“嵌套锁需求”

可重入锁的设计主要是为了​​简化复杂业务逻辑中的锁管理​​。在真实业务中,嵌套调用(如方法A调用方法B,两者都需要同一把锁)非常常见,若使用不可重入锁,开发者需手动维护锁的获取次数(例如,通过计数器记录嵌套层级),否则容易因忘记释放锁或重复释放导致死锁或数据不一致。

1. 不可重入锁的痛点

假设使用不可重入锁,开发者需手动处理嵌套调用的锁逻辑:

public class NonReentrantLockExample {private int lockCount = 0; // 手动维护重入次数private final Object lock = new Object();public void methodA() {synchronized (lock) { // 不可重入锁,第一次加锁lockCount++;try {// 业务逻辑...methodB(); // 调用methodB} finally {lockCount--;if (lockCount == 0) {// 手动释放锁(仅当重入次数为0时)}}}}public void methodB() {synchronized (lock) { // 不可重入锁,此处会阻塞!// 业务逻辑...}}
}

这种情况下,methodBsynchronized块会因无法获取锁而永久阻塞,必须通过复杂的计数器逻辑手动管理锁的释放,容易出错。

2. 可重入锁的简化

Redisson的可重入锁通过​​自动维护重入次数​​解决了这一问题:

  • 当同一线程首次获取锁时,重入次数初始化为1;
  • 若同一线程再次获取同一把锁(如嵌套调用),重入次数递增(如2、3...);
  • 释放锁时,重入次数递减,直到次数为0时才真正释放锁(通知Redis删除锁)。

开发者无需手动维护重入次数,锁的获取和释放逻辑与普通单次加锁一致,大大降低了复杂度。


三、锁值(重入次数)的含义:“当前线程持有锁的次数”

Redisson的可重入锁在Redis中存储的键值对结构大致如下(通过RedissonLock类的tryLock方法实现的Lua脚本):

-- 锁的键:lockKey(如"lock:user:123")
-- 锁的值:持有者ID(如线程ID+客户端ID) + 重入次数(初始为1)
if not redis.call('exists', KEYS[1]) thenredis.call('hset', KEYS[1], ARGV[2], 1) -- 持有者ID -> 重入次数1redis.call('pexpire', KEYS[1], ARGV[1]) -- 设置超时时间return 1
elseif redis.call('hexists', KEYS[1], ARGV[2]) == 1 thenlocal count = redis.call('hincrby', KEYS[1], ARGV[2], 1) -- 重入次数+1redis.call('pexpire', KEYS[1], ARGV[1]) -- 重置超时时间(防止过期)return nil
elsereturn 0
end

其中,​​锁的值(存储在Redis的Hash结构中)​​ 包含两部分:

  • ​持有者ID​​:唯一标识当前持有锁的线程(如thread:123@client:456,避免不同节点的线程ID冲突);
  • ​重入次数​​:当前线程已获取该锁的次数(初始为1,每次重入+1,释放时-1)。
1. 锁值≠0的含义

当锁值(重入次数)​​大于0​​时,说明​​当前线程仍持有该锁​​(可能是在嵌套调用中,或尚未释放所有重入次数)。此时,其他线程尝试获取该锁会被阻塞(直到锁超时或当前线程释放)。

2. 锁值=0的含义

当锁值​​等于0​​时,说明当前线程已完全释放该锁(所有重入次数已耗尽),Redis会删除该锁的键,其他线程可以重新竞争获取。


四、总结:可重入锁的本质是“同一线程的锁重用”

Redisson可重入锁的“可重入”核心是​​允许同一线程多次获取同一把锁​​,解决的是复杂业务中嵌套调用(如递归、方法调用链)导致的锁冲突问题。其本质是“同一线程的锁重用”,而非“不同业务的锁共享”。

锁值(重入次数)记录了当前线程持有锁的次数,只有当次数减至0时,锁才真正释放。这一设计避免了开发者手动维护嵌套锁的复杂性,是分布式系统中处理复杂业务逻辑的重要工具。

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

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

相关文章

【AI】联网模式

【AI】联网模式 文章目录【AI】联网模式1. 简介2. 接入步骤2.1 引入依赖2.2 方法构建2.3 接口构建1. 简介 在使用联网模式之前,我们如果问起ai一些最近网络上流传的一些东西,它可能并不能准确的给你描述出来,因为它的知识库更新时间可能停留…

第10篇:实战验收篇

🔍 实战演练:多条件房源查询 需求描述 查找一套符合以下条件的房子: 预算:2000–3000元区域:天河区户型:两房 关键词:多条件查询 AND BETWEEN LIKE 组合运用🎬 开场白“听起来不难&a…

深入解析YARN中的FairScheduler与CapacityScheduler:资源分配策略的核心区别

YARN资源调度器概述在Hadoop生态系统中,YARN(Yet Another Resource Negotiator)作为核心资源管理平台,其架构设计将计算资源管理与作业调度解耦,形成了"全局资源管理器(ResourceManager)节…

基于Seata的微服务分布式事务实战经验分享

基于Seata的微服务分布式事务实战经验分享 1. 业务场景描述 在电商系统中,用户下单会涉及多个微服务:订单服务(Order Service)、库存服务(Inventory Service)、账户服务(Account Service&#x…

Linux库——库的制作和原理(2)_库的原理

文章目录库的原理理解目标文件ELF文件读取ELF的工具——readelfELF从形成到加载的轮廓ELF形成可执行文件ELF可执行的加载理解链接与加载静态链接ELF加载和进程地址空间虚拟地址 & 逻辑地址重新理解进程地址空间动态链接和动态库的加载进程如何找到动态库多个进程之间如何共…

Redis C++客户端——通用命令

目录 代码案例 get和set部分 exists部分 del部分 keys部分 expire部分 type部分 本篇文章主要是通过redis-plus-plus库使用通用命令。 代码案例 下面用一个代码演示&#xff1a; #include <sw/redis/redis.h> #include <iostream> #include <vecto…

手机开启16k Page Size

我买了一个pixel8的手机&#xff0c;系统是Android16,如下操作都是基于这个手机做的。 https://source.android.com/docs/core/architecture/16kb-page-size/16kb-developer-option?hlzh-cn#use_16kb_toggle 使用 16 KB 切换开关 按照开发者选项文档中的指示启用开发者选项。…

VLAN的划分(基于华为eNSP)

VLAN的划分 前言&#xff1a;为什么VLAN是现代网络的“隐形骨架”&#xff1f; 当一台办公室电脑发送文件给隔壁工位的同事时&#xff0c;数据如何精准抵达目标而不“打扰”其他设备&#xff1f;当企业财务部的敏感数据在网络中传输时&#xff0c;如何避免被其他部门的设备“窥…

从压缩到加水印,如何实现一站式图片处理

当你需要对大量图片进行相同或相似的操作时&#xff08;例如压缩、裁剪、调整尺寸、添加水印等&#xff09;&#xff0c;逐个处理会非常耗时。批量处理工具可以一次性处理数百张图片&#xff0c;大大节省了时间。这是一款极致轻巧的图片处理利器&#xff0c;体积仅有652KB&…

Pythong高级入门Day5

二、面向对象编程面向对象编程&#xff08;Object-Oriented Programming&#xff0c;简称OOP&#xff09;是一种通过组织对象来设计程序的编程方法。Python天生就是面向对象的模块化编程。1. 初识类和对象示意图&#xff1a;/-------> BYD E6(京A.88888) 实例&#xff0c;对…

C#其他知识点

接口类---interface什么是接口? 在接口当中一般我们认为接口中的成员都是抽象的。接口一般认为是功能的集合。在接口类当中定义的方法都是抽象象方法。(没有方法体)接口一般我们认为它是一种标准,一种规范,一种约定。给子类或者是派生类制定规范,规定,标准。当子类继承了该接口…

Maven 环境配置全攻略:从入门到实战

一、Maven 简介 Maven 是一个基于项目对象模型 (POM) 的项目管理工具&#xff0c;它可以通过一小段描述信息来管理项目的构建、报告和文档。 除了强大的程序构建能力外&#xff0c;Maven 还提供了高级项目管理功能。其默认构建规则具有很高的可重用性&#xff0c;通常只需两三…

现代 C++ 开发工作流(VSCode / Cursor)

✅ 推荐的现代 C 开发工作流&#xff08;含 VSCode / Cursor 插件配置&#xff09;&#x1f9f0; 一、环境要求 C 编译器&#xff08;如 g 或 clang&#xff09;CMake&#xff08;建议 ≥ 3.16&#xff09;clangd&#xff08;建议 ≥ 14&#xff0c;最好用系统包管理器安装&…

[SAP ABAP] ALV报表练习4

SO销售订单明细报表业务目的&#xff1a;根据选择屏幕的筛选条件&#xff0c;使用ALV报表显示销售订单详情(Sales Order、Material、现有Qty、已开立数量以及剩余数量等)信息效果展示我们在销售订单栏位输入需要查询的SO单号&#xff0c;这里我们以SO单号0000000221为例&#x…

《设计模式之禅》笔记摘录 - 10.装饰模式

装饰模式的定义装饰模式(Decorator Pattern)是一种比较常见的模式&#xff0c;其定义如下&#xff1a;Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending fu…

[AI8051U入门第十步]W5500-客户端

学习目标: 1、认识W5500模块 2、驱动W5500静态获取ip 3、获取全球唯一码作为mac地址 4、拔出网线重插网线自动获取IP 5、编写W5500作为客户端进行TCP/IP代码一、W5500介绍 W5500 是一款由韩国 WIZnet 公司推出的高性能 硬件 TCP/IP 嵌入式以太网控制器,专为嵌入式系统设计,…

UNETR++: Delving Into Efficient and Accurate 3D Medical Image Segmentation

摘要得益于Transformer模型的成功&#xff0c;近期研究开始探索其在3D医学分割任务中的适用性。在Transformer模型中&#xff0c;自注意力机制是核心构建模块之一&#xff0c;与基于局部卷积的设计相比&#xff0c;它致力于捕捉长距离依赖关系。然而&#xff0c;自注意力操作存…

Kotlin Flow 在 Jetpack Compose 中的正确打开方式:SharedFlow vs StateFlow 与 LaunchedEffect

在 Jetpack Compose 中&#xff0c;Kotlin Flow 是处理异步数据流的核心工具&#xff0c;而 SharedFlow 和 StateFlow 是最常用的两种 Flow 类型。但很多开发者对它们的适用场景、如何与 LaunchedEffect 配合使用存在困惑。本文将深入探讨它们的区别&#xff0c;并给出最佳实践…

嵌入式——C语言:指针①

一、指针特点1.让代码更加简洁高效2.提供直接访问内存的操作3.利用指针可以直接操作硬件二、指针概念&#xff08;一&#xff09;地址&#xff1a;为了区分内存中不同字节的编号&#xff08;0到2^16-1&#xff09;&#xff08;二&#xff09;指针&#xff1a;指针就是地址&…

RabbitMQ—HAProxy负载均衡

上篇文章&#xff1a; RabbitMQ—仲裁队列https://blog.csdn.net/sniper_fandc/article/details/149312579?fromshareblogdetail&sharetypeblogdetail&sharerId149312579&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link 目录 1 HAProxy安装…