Redisson提供了分布式和可扩展的Java数据结构,包括分布式锁的实现。

1. 添加依赖

pom.xml中添加Redisson依赖:

<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.16.4</version> <!-- 根据需要选择最新稳定版本 -->
</dependency>
2. 配置Redisson客户端

创建Redisson配置类:

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {// 创建单机模式的配置Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setPassword("yourPassword") // 如果有密码.setDatabase(0);// 集群模式配置示例/*config.useClusterServers().addNodeAddress("redis://node1:7000", "redis://node2:7001").setPassword("password").setScanInterval(2000); // 集群状态扫描间隔*/return Redisson.create(config);}
}
3. 使用Redisson分布式锁

以下是几种常见的锁使用方式:

3.1 可重入锁(Reentrant Lock)
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class OrderService {@Autowiredprivate RedissonClient redissonClient;public void createOrder(String orderId) {String lockName = "order-lock:" + orderId;RLock lock = redissonClient.getLock(lockName);try {// 方式1:阻塞式获取锁(默认30秒自动释放)lock.lock();// 方式2:带超时的获取锁(等待100秒,自动释放时间30秒)// boolean isLocked = lock.tryLock(100, 30, TimeUnit.SECONDS);// if (isLocked) { ... }// 执行业务逻辑System.out.println("获取锁成功,处理订单: " + orderId);Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 释放锁lock.unlock();}}
}
3.2 公平锁(Fair Lock)
public void createOrderFair(String orderId) {String lockName = "fair-order-lock:" + orderId;// 获取公平锁RLock fairLock = redissonClient.getFairLock(lockName);try {fairLock.lock();// 执行业务逻辑} finally {fairLock.unlock();}
}
3.3 读写锁(ReadWriteLock)
import org.redisson.api.RReadWriteLock;public void updateProductStock(String productId) {String lockName = "product-rw-lock:" + productId;RReadWriteLock rwLock = redissonClient.getReadWriteLock(lockName);// 写锁(互斥)try {rwLock.writeLock().lock();// 执行写操作,如更新库存} finally {rwLock.writeLock().unlock();}// 读锁(共享)try {rwLock.readLock().lock();// 执行读操作,如查询库存} finally {rwLock.readLock().unlock();}
}
4. Redisson分布式锁的特性
  1. 自动续约(WatchDog机制)
    当获取锁时未指定释放时间,Redisson会自动延长锁的过期时间,防止业务执行时间过长导致锁提前释放。

  2. 可重入性
    同一线程可以多次获取同一把锁而不会死锁,需要相应次数的释放。

  3. 异常处理
    Redisson的锁释放操作在finally块中执行,确保即使发生异常也能释放锁。

  4. 异步和响应式支持
    支持异步获取锁和响应式编程模型:

    // 异步获取锁
    CompletableFuture<Boolean> future = lock.tryLockAsync(100, 30, TimeUnit.SECONDS);
    
5. RedLock算法实现(多节点模式)

RedLock用于在多个Redis节点上实现更高可靠性的分布式锁:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;public void redLockExample() {// 创建多个Redisson客户端连接不同节点RedissonClient client1 = Redisson.create(...);RedissonClient client2 = Redisson.create(...);RedissonClient client3 = Redisson.create(...);RLock lock1 = client1.getLock("lock1");RLock lock2 = client2.getLock("lock2");RLock lock3 = client3.getLock("lock3");// 创建RedLockorg.redisson.api.RedLock redLock = new org.redisson.api.RedLock(lock1, lock2, lock3);try {// 尝试获取锁(多数节点成功才算成功)boolean isLocked = redLock.tryLock(100, 30, TimeUnit.SECONDS);if (isLocked) {// 执行业务逻辑}} finally {redLock.unlock();}
}

6.可重入锁示例

6.1示例背景及需求

在很多实际业务场景中,我们执行的操作可能由于各种临时原因(比如网络抖动、资源暂时不可用等)而失败,此时往往希望能够进行重试,以增加操作最终成功的概率。同时,为了保证在重试过程中对共享资源的访问是互斥的(避免多个线程同时操作造成数据不一致等问题),就需要使用可重入锁来进行控制。

6.2详细代码示例
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class RetryableOperationService {@Autowiredprivate RedissonClient redissonClient;// 定义最大重试次数private static final int MAX_ATTEMPTS = 3;// 定义重试间隔时间(单位:毫秒)private static final long RETRY_INTERVAL = 100;public void retryableOperation(String key) {String lockName = "retry-lock:" + key;RLock lock = redissonClient.getLock(lockName);lock.lock();try {int attempts = 0;while (attempts < MAX_ATTEMPTS) {try {// 这里是具体要执行的可能失败的业务操作,比如调用外部接口获取数据、更新数据库等System.out.println("正在执行第 " + (attempts + 1) + " 次尝试操作...");// 模拟业务操作可能出现的异常情况,这里简单地通过抛异常来表示操作失败if (attempts == 1) {throw new RuntimeException("模拟操作失败");}// 如果操作成功,直接返回,不再进行重试return;} catch (Exception e) {attempts++;System.out.println("操作失败,第 " + attempts + " 次重试,等待 " + RETRY_INTERVAL + " 毫秒后再次尝试...");// 线程休眠一段时间,等待后再次尝试Thread.sleep(RETRY_INTERVAL);}}System.out.println("达到最大重试次数,操作最终失败");} catch (InterruptedException ex) {Thread.currentThread().interrupt();System.out.println("线程被中断,操作终止");} finally {// 无论操作最终是否成功,都要释放锁,确保其他线程可以获取锁来执行操作lock.unlock();}}
}
6.3代码执行流程说明
  1. 获取锁
    首先,根据传入的 key 生成对应的锁名称(retry-lock:具体的key值),然后通过 RedissonClient 获取可重入锁 lock,并调用 lock() 方法获取锁,这一步保证了在同一时刻只有一个线程能够执行后续的重试操作,避免并发冲突。

  2. 执行操作与重试逻辑
    进入 while 循环,开始尝试执行具体的业务操作。在每次循环中:

    • 尝试执行业务操作:这里只是简单地模拟了业务操作,通过抛异常来表示操作失败(实际应用中可能是调用外部接口返回错误码、数据库更新失败等情况)。如果操作成功(没有抛出异常),则直接通过 return 语句结束方法,不再进行重试。
    • 处理操作失败情况:当操作抛出异常时,表示此次尝试失败。attempts 变量记录当前的重试次数,每次失败后将其加1,并输出相应的重试提示信息,然后让线程休眠 RETRY_INTERVAL(100毫秒)时间,等待一段时间后再次进行下一次尝试。
  3. 重试次数限制与最终结果处理
    while 循环的条件是 attempts < MAX_ATTEMPTS,也就是最多只会重试 MAX_ATTEMPTS(定义为3次)次。当达到最大重试次数后,如果操作还是没有成功,就会输出操作最终失败的提示信息。

  4. 释放锁
    无论操作最终是成功还是达到最大重试次数失败,都会进入 finally 块释放锁。这一步非常关键,确保了锁资源能够被正确释放,使得其他等待获取该锁的线程有机会执行相应的操作。

6.4适用场景举例
  1. 网络请求重试
    比如在一个微服务架构中,服务A需要调用服务B的接口获取数据来更新本地数据库。由于网络环境不稳定,可能出现调用失败的情况。此时,服务A就可以使用上述带有可重入锁的重试机制,在获取到锁后,多次尝试调用服务B的接口,保证在重试过程中不会有其他线程同时进行相同的调用操作,避免数据不一致等问题。

  2. 数据库更新重试
    当向数据库插入或更新一条重要记录时,可能因为数据库临时负载过高、事务冲突等原因导致操作失败。通过使用这种带可重入锁的重试机制,可以在一定次数内反复尝试更新操作,同时保证在整个重试期间,其他线程不会干扰该记录的更新,确保数据的完整性和一致性。

6.5总结

可重入锁主要用于以下方面:

  1. 确保操作的互斥性
    在整个 retryableOperation 方法执行期间,从获取锁开始到最终释放锁,只有获取到锁的那个线程能够执行后续的业务操作以及重试逻辑。例如,假设有多个线程同时调用 retryableOperation 方法并传入相同的 key,由于锁的存在,只有最先成功获取到锁(锁名为 retry-lock:具体的key值)的线程能够进入到方法内部开始执行可能失败的业务操作以及重试流程,其他线程会被阻塞等待锁释放,从而保证了针对同一 key 对应的操作在同一时刻是互斥执行的,避免了多个线程同时操作造成的数据不一致、资源竞争等问题。
  2. 支持重试过程中的再次进入
    因为是可重入锁,在重试逻辑中(while 循环内),当业务操作失败后线程进行休眠,然后再次尝试执行业务操作时,同一线程能够再次获取到已经持有的这把锁(无需等待锁释放后重新竞争获取),可以顺利进入循环体继续执行后续的操作,不会出现因为锁的限制而导致自己阻塞自己的情况。如果使用的是不可重入锁,那么在重试时线程再次尝试获取锁就会被阻塞,因为它自己已经持有这把锁了,这样就无法完成重试逻辑,会陷入死锁状态。

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

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

相关文章

Web基础关键_004_CSS(二)

目 录 一、样式 1.行内样式 2.内部样式 3.外部样式 二、选择器优先级 1.非关系选择器 2.关系选择器 三、属性 四、盒子模型 五、元素 1.块级元素 2.行内元素 3.行内块级元素 4.元素类型转换 六、浮动 七、定位 1.静态定位 2.相对定位 3.绝对定位 4.固定定位 …

数据使用权与所有权分离:能否诞生“数据租赁”市场

——首席数据官高鹏律师数字经济团队创作&#xff0c;AI辅助 数据如矿藏&#xff0c;开采需“契约” 想象一座蕴藏着无尽资源的数字矿山&#xff1a;企业或个人拥有数据的“所有权”&#xff0c;如同手握矿脉的产权&#xff0c;但若无法高效挖掘其价值&#xff0c;矿石终将沉…

【esp32s3】2 - 第一个组件

下面的内容编写时间跨度有点大&#xff0c;乱了得一团&#xff0c;也没ai整理。食之无味&#xff0c;弃之可惜。 推荐笔记&#xff1a;ESP32 之 ESP-IDF 教学&#xff08;十八&#xff09;—— 组件配置&#xff08;KConfig&#xff09; 推荐笔记&#xff1a;Kconfig 拓展 乐鑫…

【Java_EE】单例模式、阻塞队列、线程池、定时器

目录 单例模式 饿汉模式<代码> 懒汉模式<代码> 阻塞队列 阻塞队列概念 阻塞队列解决的问题 阻塞队列工作原理 阻塞队列的优/缺点 优点 缺点 模拟实现阻塞队列<代码> 线程池 线程池概念 线程池解决的问题 线程池参数 四种拒绝策略 线程池工作…

Redis初识第七期---ZSet的命令和应用场景

ZSet相较于Set来说&#xff0c;它又是有序的&#xff0c;这个有序指的就是我们通常意义上的有序了&#xff0c;ZSet内部中是按照升序来排序的。 排序规则&#xff1a;ZSet相较于Set来说&#xff0c;它内部引入了一个新的属性&#xff1a;分数&#xff08;Score&#xff09;&am…

Wps开放平台v5升级v7上传实体文件踩坑(Java使用restTemplate)

背景&#xff1a; 最近接到一个老项目需求&#xff0c;之前开发的WPS开放平台文件&#xff08;商密集成&#xff09;预览功能因为升级需要重新对接api&#xff0c;新的上传文件接口踩坑特意记录一下。 这里出问题的是第二步&#xff0c;请求文件上传信息 踩坑代码 调用后403 p…

啥时候上RAG?啥时候上微调?丨实战笔记

哈喽&#xff0c;大家好&#x1f44f; 我是阿星&#xff01; 现在很多AI科普文章都会提到微调&#xff0c;RAG。 但是没有实战的过的同学可能会问&#x1f914;—— 啥时候用RAG&#xff1f;啥时候用微调呢&#xff1f;有啥区别&#xff1f;不都是让模型增加知识面的吗&…

RabbitMQ-基础篇

前言&#xff1a; 今天开始学RabbitMQ,还是跟着黑马的课程。 今日所学&#xff1a; RabbitMQ介绍RabbitMQ入门Java客户端中的MQ 1.RabbitMQ介绍 1.1 什么是RabbitMQ RabbitMQ 是一个开源的消息代理软件&#xff08;消息队列中间件&#xff09;&#xff0c;实现了高级消息…

docker-compose配置redis哨兵详细步骤和配置文件

docker-compose配置redis哨兵详细步骤和配置文件 目录结构调整 redis-cluster/ ├── config/ │ ├── master.conf # 主节点配置 │ ├── slave1.conf # 从节点1配置 │ ├── slave2.conf # 从节点2配置 │ ├── sentinel1.…

多模态大语言模型arxiv论文略读(146)

Exploring Response Uncertainty in MLLMs: An Empirical Evaluation under Misleading Scenarios ➡️ 论文标题&#xff1a;Exploring Response Uncertainty in MLLMs: An Empirical Evaluation under Misleading Scenarios ➡️ 论文作者&#xff1a;Yunkai Dang, Mengxi G…

【教程】Linux中限制用户可以使用的GPU数量 | 附脚本

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 背景说明 设置方法 管理脚本 进阶限制 恢复默认组 注意事项 背景说明 比较简单的方式是使用group来管理权限&#xff0c;这种方式能限制哪些…

90.xilinx复位低电平(一般使用低电平复位)

Xilinx FPGA 中的寄存器&#xff08;Flip-Flop&#xff09;**确实支持异步复位**&#xff0c;但具体实现方式取决于你使用的设计方法&#xff08;HDL 代码风格或原语实例化&#xff09;。以下是详细说明&#xff1a; --- ### 1. **Xilinx 寄存器的复位特性** - **同步复位…

NVMe高速传输之摆脱XDMA设计10: DMA 控制单元设计

DMA 控制单元负责控制 DMA 传输事务&#xff0c; 该单元承担了 DMA 事务到 NVMe 事务的转换任务&#xff0c; 使用户对数据传输事务的控制更加简单快捷。 DMA 控制功能由 DMA寄存器组实现。 DMA 寄存器组包含 DMA 操作寄存器、 DMA 长度寄存器、 DMA 源目的地址寄存器和 DMA 状…

如何设置电脑定时休眠?操作指南详解

长时间运行电脑会导致硬件过热&#xff0c;缩短其使用寿命。定时关机有助于让硬件得到休息&#xff0c;降低因长时间高负荷工作导致损坏的风险。 它的界面简洁直观&#xff0c;功能却十分实用&#xff0c;涵盖了定时关机、重启、注销、休眠、待机以及锁定等多种操作。 以设置“…

LeetCode[617]合并二叉树

思路&#xff1a; 我们合并左右子树&#xff0c;在递归左右子树的时候&#xff0c;一定要保证左右子树不为空&#xff0c;如果左子树为空&#xff0c;那么直接返回右子树就行了&#xff0c;即使右子树为空。如果右子树为空那么直接返回左子树就行了&#xff0c;这样判断完就正常…

Redis 常用五大数据类型

1、Redis 关键字&#xff08;Key&#xff09; keys * 查看当前库所有keyexists [key] 判断某个key是否存在type [key] 查看当前key的数据类型del [key] 删除指定的key数据unlink [key] 根据value选择非阻塞删除&#xff0c;仅将keys从keyspace元数据中删除&#xff0c;真正的删…

大语言模型(LLM)专业术语汇总

1. 训练与部署 1.1 预训练 专业&#xff1a;在海量无标注文本&#xff08;如Common Crawl、Wikipedia&#xff09;上通过自监督学习训练基础语言模型&#xff0c;学习通用语言表征&#xff08;如GPT-3训练数据达45TB&#xff09;。通俗&#xff1a;AI的“通识教育阶段”&…

【Java Swing 图形界面编程】JList 列表组件 ① ( JList 组件简介 | 核心作用 | 关键特性 | 基础用法示例 )

文章目录 一、JList 组件简介1、JList 概念简介2、JList 核心作用3、JList 关键特性 二、JList 组件基础用法示例1、使用 String 数组构建列表项2、使用 Vector 集合构建列表项3、使用 DefaultListModel 构建列表项 一、JList 组件简介 1、JList 概念简介 基本概念 : JList 组件…

【小技巧】Python+PyCharm IDE 配置解释器出错,环境配置不完整或不兼容。(小智AI、MCP、聚合数据、实时新闻查询、NBA赛事查询)

报错信息如下&#xff1a; [unsupported] Python 3.1 (mcp-calculator-main) (3) C:\Users(xsshu\AppData\Local\Programs\Python\Python313\python.exe [unsupported] Python 3.1 C:\Users\xsshu\AppData\Local\Programs\Python\Python311\python.exe 这条输出显示了两个 Pyth…

Ragflow 前后端登录逻辑

前端登录逻辑 路由配置&#xff1a; /login 路由指向 /pages/login 组件。登录表单使用 Ant Design 的 Form, Input, 和 Button 组件。 登录表单处理&#xff1a; 使用 useLogin钩子来处理登录请求。密码通过 RSA 加密后再发送到服务器。成功登录后导航至 /knowledge 页面。 …