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

[Redis] Redisson实现分布式锁

实现分布式锁的方式有多种,例如基于数据库、Redis、ZooKeeper 等中间件来实现,它们通常依赖于这些中间件提供的事务特性,或者命令语义来达到分布式环境下的锁效果。例如,Redis 通过 SETNX 命令配合过期时间可实现一个简单的分布式锁方案。

文章目录

      • 1.SETNX 存在的问题
      • 2.Redisson 特性说明
      • 3.Redisson 使用分布式锁

1.SETNX 存在的问题

虽然可以使用 SETNX 命令方便的实现分布式锁,但是 SETNX 存在以下问题:

  • 死锁问题:SETNX 如未设置过期时间,锁忘记删了或加锁线程宕机都会导致死锁,也就是分布式锁一直被占用的情况。
  • 锁误删问题:SETNX 设置了超时时间,但因为执行时间太长,所以在超时时间之内锁已经被自动释放了,但线程不知道,因此在线程执行结束之后,会把其他线程的锁误删的问题。
  • 不可重入问题:也就是说同一线程在已经获取了某个锁的情况下,如果再次请求获取该锁,则请求会失败(因为只有在第一次能加锁成功)。也就是说,一个线程不能对自己已持有的锁进行重复锁定。
  • 无法自动续期:线程在持有锁期间,任务未能执行完成,锁可能会因为超时而自动释放。SETNX 无法自动根据任务的执行情况,设置新的超时实现,以延长锁的时间。

Redisson 是一个开源的用于操作 Redis 的 Java 框架。与 Jedis 和 Lettuce 等轻量级的 Redis 框架不同,它提供了更高级且功能丰富的 Redis 客户端。它提供了许多简化 Redis 操作的高级 API,并支持分布式对象、分布式锁、分布式集合等特性。

2.Redisson 特性说明

  1. Redisson 可以设置分布式锁的过期时间,从而避免锁一直被占用而导致的死锁问题。
  2. Redisson 在为每个锁关联一个线程 ID 和重入次数(递增计数器)作为分布锁 value 的一部分存储在 Redis 中,这样就避免了锁误删和不可重入的问题。
  3. Redisson 还提供了自动续期的功能,通过定时任务(看门狗)定期延长锁的有效期,确保在业务未完成前,锁不会被其他线程获取。

3.Redisson 使用分布式锁

maven:

<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.25.2</version> <!-- 请根据实际情况使用最新版本 -->
</dependency>

Condig:

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();// 也可以将 redis 配置信息保存到配置文件config.useSingleServer().setAddress("redis://127.0.0.1:6379");return Redisson.create(config);}
}

Controller:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class LockController {@Autowiredprivate RedissonClient redissonClient;@GetMapping("/lock")public String lockResource() throws InterruptedException {String lockKey = "myLock";// 获取 RLock 对象RLock lock = redissonClient.getLock(lockKey);try {// 尝试获取锁(尝试加锁)(锁超时时间是 30 秒)boolean isLocked = lock.tryLock(30, TimeUnit.SECONDS);if (isLocked) {// 成功获取到锁try {// 模拟业务处理TimeUnit.SECONDS.sleep(5);return "成功获取锁,并执行业务代码";} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放锁lock.unlock();}} else {// 获取锁失败return "获取锁失败";}} catch (InterruptedException e) {e.printStackTrace();}return "获取锁成功";}
}

Redisson 分布式锁的操作和 Java 中的 ReentrantLock(可重入锁)的操作很像,都是先使用 tryLock 尝试获取(非公平)锁,最后再通过 unlock 释放锁。

1.实现公平锁

Redisson 默认创建的分布式锁是非公平锁(出于性能的考虑),想要把它变成公平锁可使用以下代码实现:

RLock lock = redissonClient.getFairLock(lockKey);  

2.实现读写锁

Redisson 还可以创建读写锁,如下代码所示:

RReadWriteLock lock = redissonClient.getReadWriteLock(lockKey); // 获取读写锁
lock.readLock();  // 读锁
lock.writeLock(); // 写锁

读写锁的特点就是并发性能高,它是允许多个线程同时获取读锁进行读操作的,也就是说在没有写锁的情况下,读取操作可以并发执行,提高了系统的并行度。但写锁则是独占式的,同一时间只有一个线程可以获得写锁,无论是读还是写都无法与写锁并存,这样就确保了数据修改时的数据一致性。

3.实现联锁

Redisson 也支持联锁,也叫分布式多锁 MultiLock,它允许客户端一次性获取多个独立资源(RLock)上的锁,这些资源可能是不同的键或同一键的不同锁。当所有指定的锁都被成功获取后,才会认为整个操作成功锁定。这样能够确保在分布式环境下进行跨资源的并发控制。联锁的实现示例如下:

// 获取需要加锁的资源
RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
// 联锁
RedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2);
try {// 一次性尝试获取所有锁if (multiLock.tryLock()) {// 获取锁成功...}
} finally {// 释放所有锁multiLock.unlock();
}

相关文章:

  • 车载 Android之 核心服务 - CarPropertyService 的VehicleHAL
  • 构建自己的私人GPT
  • FTP服务器安装、远程访问以及安全配置项
  • 外包干了1个月,技术退步一大半。。。
  • 胡圆圆的暑期实习经验分享
  • Spark回归分析与特征工程
  • 数据库攻防学习之MySQL
  • 2024年阿里云、腾讯云、华为云、LightNode、硅云服务器如何选?怎么买最划算?[最新价格表]
  • Node.js中的模块,常用模块具体代码示例
  • IDEA TODO
  • Mac环境下反编译apk
  • 高性能NVMe Host Controller IP
  • 翻译!翻译!AI是什么?
  • python 写自动点击爬取数据
  • 2024年Mac专用投屏工具AirServer 7 .27 for Mac中文版
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • JavaScript服务器推送技术之 WebSocket
  • Java多态
  • Java多线程(4):使用线程池执行定时任务
  • java中具有继承关系的类及其对象初始化顺序
  • JSONP原理
  • Linux链接文件
  • MySQL主从复制读写分离及奇怪的问题
  • Nacos系列:Nacos的Java SDK使用
  • Nodejs和JavaWeb协助开发
  • RxJS: 简单入门
  • Sublime Text 2/3 绑定Eclipse快捷键
  • 电商搜索引擎的架构设计和性能优化
  • 对象引论
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 力扣(LeetCode)56
  • 悄悄地说一个bug
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 延迟脚本的方式
  • 译米田引理
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • #、%和$符号在OGNL表达式中经常出现
  • #100天计划# 2013年9月29日
  • (04)odoo视图操作
  • (floyd+补集) poj 3275
  • (附源码)计算机毕业设计高校学生选课系统
  • * CIL library *(* CIL module *) : error LNK2005: _DllMain@12 already defined in mfcs120u.lib(dllmodu
  • ./configure,make,make install的作用(转)
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .net core 调用c dll_用C++生成一个简单的DLL文件VS2008
  • .NET Framework与.NET Framework SDK有什么不同?
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • .w文件怎么转成html文件,使用pandoc进行Word与Markdown文件转化
  • .xml 下拉列表_RecyclerView嵌套recyclerview实现二级下拉列表,包含自定义IOS对话框...
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • @拔赤:Web前端开发十日谈
  • [<事务专题>]