当前位置: 首页 > news >正文

分布式锁-Redisson 可重入锁

在分布式系统中,分布式锁是一种常用的机制,用来在多个实例或服务之间控制共享资源的访问,确保不会出现并发冲突或资源竞争。Redis 是常用的分布式锁实现工具,而 Redisson 是 Redis 的一个 Java 客户端,它提供了强大且易用的分布式锁功能,包括可重入锁、读写锁、公平锁等。

1. 什么是可重入锁

可重入锁(Reentrant Lock),也称为递归锁,是指同一个线程在持有锁的情况下,可以再次获取该锁而不会被锁住。这种锁允许线程重复进入被它锁定的代码块,避免死锁问题。

可重入锁的一个典型应用场景是,某个方法在持有锁时,调用了另一个也需要获取该锁的方法。如果没有可重入特性,这种情况下将会造成死锁。

在 Redisson 中,可重入锁的核心功能是允许线程在持有锁的情况下,能够多次请求同一个锁,而不需要手动释放锁多次。

2. Redisson 可重入锁的特点

  • 可重入性:同一线程可以多次获取同一个锁,锁的计数器会递增。
  • 自动续期:默认情况下,Redisson 的锁会自动为持有锁的线程续期,确保锁在超时前不会被意外释放。
  • 高可用:通过 Redis 集群,Redisson 的分布式锁可以在分布式环境中保证高可用性,支持故障恢复和锁状态的持久化。

3. Redisson 可重入锁的使用

3.1 引入 Redisson 依赖

在 Spring Boot 项目或其他 Java 项目中使用 Redisson,可以通过 Maven 引入 Redisson 的依赖:

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.4</version>
</dependency>
3.2 配置 Redisson 客户端

Redisson 需要连接 Redis 服务器。可以通过 redisson.yaml 文件进行配置,或在代码中手动配置 Redis 连接。

使用 YAML 文件进行配置(redisson.yaml):

singleServerConfig:address: "redis://127.0.0.1:6379"password: nulldatabase: 0

在 Spring Boot 中加载该配置文件:

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 = Config.fromYAML(RedissonConfig.class.getClassLoader().getResource("redisson.yaml"));return Redisson.create(config);}
}
3.3 使用 Redisson 实现可重入锁

一旦 Redisson 客户端配置好,就可以在代码中使用 Redisson 提供的分布式锁 API。下面是使用可重入锁的示例:

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 LockService {@Autowiredprivate RedissonClient redissonClient;public void executeWithLock() {// 获取分布式可重入锁RLock lock = redissonClient.getLock("myLock");try {// 尝试加锁,等待 10 秒,10 秒后自动释放if (lock.tryLock(10, 10, TimeUnit.SECONDS)) {try {// 执行业务逻辑System.out.println("业务逻辑执行中...");// 模拟内部调用方法,重入锁nestedLockMethod();} finally {// 释放锁lock.unlock();}}} catch (InterruptedException e) {e.printStackTrace();}}// 模拟重入锁的情况private void nestedLockMethod() {RLock lock = redissonClient.getLock("myLock");lock.lock();try {System.out.println("内部方法执行...");} finally {lock.unlock();}}
}

在上述代码中:

  • redissonClient.getLock("myLock") 获取一个可重入锁。
  • tryLock(10, 10, TimeUnit.SECONDS) 表示尝试获取锁,最多等待 10 秒,如果获取成功,锁将在 10 秒后自动释放。
  • nestedLockMethod() 方法中,模拟了重入锁的情况,同一个线程再次获取了同一个锁,而不会出现阻塞。
3.4 自动续期机制

Redisson 的分布式锁有一个非常重要的功能:自动续期。当一个线程获取了锁后,Redisson 会默认启动一个守护线程,为该锁的有效期进行续期,防止锁在执行长时间任务时被自动释放。

默认情况下,Redisson 会在锁被持有时每隔 30 秒续期,确保锁不会因为超时而被误释放。如果业务逻辑执行时间较长,这个功能可以防止锁过期失效。

自动续期的实现方式是,Redisson 在后台定期检查持有锁的线程是否仍然存活,如果线程仍在运行,它会延长锁的过期时间。

3.5 手动设置锁的超时时间

尽管 Redisson 提供了自动续期功能,但在某些场景下,开发者可能希望手动设置锁的有效时间。可以通过 lock() 方法的重载版本设置锁的超时时间:

// 设置锁的自动释放时间为 30 秒
lock.lock(30, TimeUnit.SECONDS);

这种方式适合业务逻辑执行时间较短且可以预估的场景。如果在设定的超时时间内没有释放锁,Redisson 会自动释放该锁。

4. Redisson 可重入锁的工作原理

4.1 Redis 中的锁实现

Redisson 的可重入锁是基于 Redis 的 SET 命令和 Lua 脚本实现的。当线程获取锁时,Redisson 会在 Redis 中执行类似如下的命令:

SET myLock <random-value> NX PX 10000
  • NX:表示只有当键不存在时才会设置,确保分布式锁的原子性。
  • PX:设置锁的有效期(过期时间),单位为毫秒。

可重入锁通过给每个线程生成一个唯一的标识(random-value)来区分不同线程的锁。如果同一个线程多次获取锁,Redisson 会使用计数器来跟踪锁的重入次数,释放锁时只会在计数器归零时才真正释放。

4.2 解锁的实现

解锁操作也需要保证线程安全,Redisson 通过 Lua 脚本确保只有持有锁的线程才能成功解锁。Lua 脚本可以保证解锁操作的原子性:

if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])
elsereturn 0
end

在解锁时,Redisson 会检查 Redis 中存储的锁标识是否与当前线程匹配,只有匹配时才会删除锁。

5. Redisson 可重入锁的应用场景

可重入锁在以下场景中非常有用:

  • 分布式资源争用:多个服务实例或线程同时访问共享资源,如数据库、文件系统、第三方服务等,使用分布式锁可以确保这些资源不会被并发修改。
  • 任务调度和执行:在分布式任务调度系统中,确保同一时间只有一个实例在处理某个任务,防止任务重复执行。
  • 库存管理和订单处理:在电商系统中,避免多个请求同时操作同一件商品的库存,导致超卖问题。

6. 注意事项

  • 锁的自动续期:Redisson 默认的自动续期功能适合大部分场景,但开发者应确保持有锁的业务逻辑不会意外地被卡住或进入死循环。
  • 合理设置超时时间:在使用 tryLock() 或手动设置锁的超时时间时,应根据业务需求合理设置,避免锁的有效期太长或太短。
  • 持久化与高可用:确保 Redis 服务器的高可用性,Redisson 锁依赖 Redis 的可靠性,建议使用 Redis 集群或主从架构。

7.

总结

Redisson 为 Java 开发者提供了强大的分布式锁功能,特别是可重入锁的实现,使得在分布式系统中可以轻松实现线程间和服务实例间的资源竞争控制。通过 Redisson 的可重入锁,开发者可以确保同一线程在获取锁后能够多次进入锁定区域,避免死锁。同时,Redisson 的自动续期功能确保了锁在长时间任务中的可靠性。配合 Redis 的高性能和高可用性,Redisson 是分布式环境中实现锁机制的理想选择。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 注意力机制的细节
  • redis群集的三种模式
  • Jenkins 通过 Version Number Plugin 自动生成和管理构建的版本号
  • crm如何做私域运营?
  • Harmony Next 文件命令操作(发送、读取、媒体文件查询)
  • 【Hot100】LeetCode—215. 数组中的第K个最大元素
  • Qt常用控件——QLineEdit
  • uts+uniapp踩坑记录(vue3项目
  • 美团面试题:生成字符串的不同方式
  • 期权有哪些开户免50万元验资的方法?怎么操作?
  • 《C++位域:在复杂数据结构中的精准驾驭与风险规避》
  • uniapp微信小程序开发踩坑日记:Pinia持久化报错Cannot read property ‘localStorage‘ of undefined
  • map与set
  • 基于SpringBoot的医院挂号预约管理系统
  • vulnhub靶机:Holynix: v1
  • php的引用
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 【347天】每日项目总结系列085(2018.01.18)
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • Java反射-动态类加载和重新加载
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • nfs客户端进程变D,延伸linux的lock
  • Protobuf3语言指南
  • Puppeteer:浏览器控制器
  • scrapy学习之路4(itemloder的使用)
  • windows下mongoDB的环境配置
  • 从0实现一个tiny react(三)生命周期
  • 记一次和乔布斯合作最难忘的经历
  • 一些css基础学习笔记
  • 移动端 h5开发相关内容总结(三)
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • #Linux(Source Insight安装及工程建立)
  • $.ajax,axios,fetch三种ajax请求的区别
  • $.ajax中的eval及dataType
  • (C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。
  • (Forward) Music Player: From UI Proposal to Code
  • (function(){})()的分步解析
  • (Java)【深基9.例1】选举学生会
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (四)库存超卖案例实战——优化redis分布式锁
  • (五)activiti-modeler 编辑器初步优化
  • (一)十分简易快速 自己训练样本 opencv级联haar分类器 车牌识别
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)shell中括号的特殊用法 linux if多条件判断
  • .describe() python_Python-Win32com-Excel
  • .gitignore文件---让git自动忽略指定文件
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .NET Framework .NET Core与 .NET 的区别
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .Net 应用中使用dot trace进行性能诊断
  • .NET 中让 Task 支持带超时的异步等待
  • .NET企业级应用架构设计系列之应用服务器
  • .Net中的集合