如何用word做网站,做网站的销售好做吗,网上推销产品的软件,速购单微信小程序怎么做文章目录 引言1. Redisson概述1.1 Redisson的基本概念1.2 Redisson的主要功能1.3 Redisson的优点 2. 开发环境3. Redisson的安装与配置3.1 添加依赖3.2 配置Redisson 4. 使用Redisson4.1 可重入锁4.1.1 可重入锁的概念4.1.2 可重入锁的实现原理4.1.3 简单使用锁的获取和释放 4.… 文章目录 引言1. Redisson概述1.1 Redisson的基本概念1.2 Redisson的主要功能1.3 Redisson的优点 2. 开发环境3. Redisson的安装与配置3.1 添加依赖3.2 配置Redisson 4. 使用Redisson4.1 可重入锁4.1.1 可重入锁的概念4.1.2 可重入锁的实现原理4.1.3 简单使用锁的获取和释放 4.2 公平锁4.2.1 公平锁的概念4.2.2 公平锁的实现原理4.2.3 简单使用 4.3 读写锁4.3.1 读写锁的概念4.3.2 读写锁的实现原理4.3.3 简单使用 4.4 联锁4.4.1 联锁的概念4.4.2 联锁的实现原理4.4.3 简单使用 5. WatchDog机制6. 总结 引言
在分布式系统中经常需要对共享资源进行并发访问控制以确保数据的一致性和完整性。分布式锁是一种用于在分布式环境中控制对共享资源访问的机制它可以保证在同一时刻只有一个客户端能够访问某些特定资源。
1. Redisson概述
1.1 Redisson的基本概念
Redisson是一个基于Redis的Java客户端它不仅提供了对Redis的基础操作支持还封装了许多高级功能如分布式锁、分布式集合、分布式队列等。Redisson的设计目标是简化分布式系统的开发提高开发效率和系统的可维护性。 1.2 Redisson的主要功能
分布式锁支持可重入锁、公平锁、读写锁、红锁等多种分布式锁机制保证分布式环境下的资源访问控制。比如在电商系统中防止超卖现象在订单系统中防止同一订单被多次处理。分布式集合提供分布式Set、List、Map等集合类型支持高并发环境下的数据操作。分布式队列支持分布式阻塞队列、延迟队列等适用于任务调度和消息传递场景。分布式对象提供分布式AtomicLong、AtomicDouble、CountDownLatch、Semaphore等对象简化分布式系统的开发。分布式服务支持分布式执行器、分布式调度器等服务增强分布式系统的功能。
1.3 Redisson的优点
Redisson是一个基于Redis的Java客户端提供了许多高级特性和分布式数据结构。相比其他Redis客户端Redisson的优势在于
简洁易用提供了丰富的API简化了分布式编程的复杂性。高可用性支持多种Redis部署模式包括单节点、主从复制和集群模式。分布式对象提供了分布式锁、分布式集合、分布式队列等高级数据结构便于在分布式环境中使用。自动续期Redisson的Watchdog机制可以自动续期分布式锁避免锁超时问题。
2. 开发环境
JDK版本JDK 17Spring Boot版本Spring Boot 3.2.2Redis版本5.0.14.1构建工具Maven
3. Redisson的安装与配置
3.1 添加依赖
dependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.24.3/version
/dependency3.2 配置Redisson 配置参考文档2. Configuration · redisson/redisson Wiki (github.com) 添加配置类RedissonConfig
/*** Redisson配置类用于配置Redisson客户端。*/
Configuration
public class RedissonConfig {/*** 创建并配置RedissonClient Bean。* * return 配置好的RedissonClient实例*/Beanpublic RedissonClient redissonClient() {// 创建Redisson配置对象Config config new Config();// 配置单节点模式config.useSingleServer()// 设置Redis服务器地址.setAddress(redis://127.0.0.1:6379)// 设置Redis服务器密码.setPassword(123321)// 设置连接池大小.setConnectionPoolSize(64)// 设置最小空闲连接数.setConnectionMinimumIdleSize(24)// 设置空闲连接超时时间毫秒.setIdleConnectionTimeout(10000)// 设置连接超时时间毫秒.setConnectTimeout(10000)// 设置命令等待超时时间毫秒.setTimeout(3000)// 设置命令重试次数.setRetryAttempts(3)// 设置命令重试间隔时间毫秒.setRetryInterval(1500);// 创建并返回RedissonClient实例return Redisson.create(config);}
}4. 使用Redisson 官方wiki文档8. Distributed locks and synchronizers · redisson/redisson Wiki (github.com) 中文版wiki文档已经有5年没有更新了不建议看8. 分布式锁和同步器 · redisson/redisson Wiki (github.com) 4.1 可重入锁
4.1.1 可重入锁的概念
可重入锁Reentrant Lock是一种允许同一个线程多次获取同一把锁的锁机制。也就是说当一个线程已经持有某个锁时它可以再次获取该锁而不会被阻塞。这种锁机制能够避免死锁问题并简化锁的使用。
可重入锁的主要特点是
同一线程可多次获取同一个线程可以多次获取同一把锁而不会被阻塞。计数器维护可重入锁内部维护一个计数器每次获取锁时计数器加1每次释放锁时计数器减1当计数器为0时锁才真正被释放。
4.1.2 可重入锁的实现原理
可重入锁的实现通常依赖于一个计数器和一个持有锁的线程标识。当一个线程第一次获取锁时计数器加1并记录持有锁的线程标识。当同一个线程再次获取锁时只需将计数器加1而不会阻塞线程。当线程释放锁时计数器减1当计数器为0时锁才真正被释放并允许其他线程获取锁。
在Redisson中可重入锁的实现基于Redis的原子操作和Lua脚本。Redisson通过维护一个计数器和持有锁的线程标识实现了可重入锁的功能。
4.1.3 简单使用
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;/*** 服务类示例*/
RequiredArgsConstructor
Service
public class XXXXService {private final RedissonClient redissonClient; /*** 使用 Redisson 可重入锁执行任务*/public void performTaskWithLock() {// 获取可重入锁对象指定锁的名称RLock lock redissonClient.getLock(myLock); try {// 尝试获取锁参数分别是获取锁的最大等待时间锁自动释放时间时间单位返回值为是否获取锁成功boolean isLock lock.tryLock(1, 10, TimeUnit.SECONDS); // 判断获取锁成功if (isLock) { try {System.out.println(执行业务); // 在这里编写需要进行锁保护的业务逻辑} finally {// 释放锁lock.unlock(); }} else {// 获取锁失败可以进行相应的处理例如记录日志或返回错误信息System.err.println(获取锁失败); }} catch (InterruptedException e) {// 处理中断异常throw new RuntimeException(e); }}
}在上述代码中我们使用redissonClient.getLock(myLock)获取一个分布式锁对象然后使用lock.tryLock()方法尝试获取锁并在任务完成后释放锁。 锁的获取和释放 获取锁使用RLock对象的tryLock()或lock()方法来获取锁。tryLock()方法允许设置等待时间和锁的自动释放时间。释放锁使用RLock对象的unlock()方法来释放锁。确保在finally块中释放锁以避免死锁。 4.2 公平锁
4.2.1 公平锁的概念
公平锁Fair Lock是一种确保锁的获取顺序与请求顺序相同的锁机制。即先请求锁的线程优先获取锁后请求的线程只能在前面的线程释放锁后才能获取锁。这种机制可以避免“饥饿”现象确保每个线程都能公平地获取锁。
4.2.2 公平锁的实现原理
公平锁的实现通常依赖于一个队列来记录请求锁的顺序。每次有线程请求锁时会将其添加到队列中当锁被释放时从队列中按照请求顺序依次唤醒等待的线程。
在Redisson中公平锁的实现基于Redis的有序集合Sorted Set和Lua脚本。每次请求锁时线程会被添加到一个有序集合中并按照时间戳排序。当锁被释放时按照有序集合中的顺序依次唤醒等待的线程。
4.2.3 简单使用
public void performTaskWithFairLock() {// 1. 获取公平锁对象RLock fairLock redissonClient.getFairLock(myFairLock);try {// 2. 尝试获取锁boolean isLock fairLock.tryLock(1, 10, TimeUnit.SECONDS); // 3. 判断是否获取到锁if (isLock) { try {System.out.println(获得公平锁正在执行任务...);// 执行任务} finally {// 4. 释放锁fairLock.unlock(); System.out.println(释放公平锁。);}} else {System.out.println(无法获取公平锁。);}} catch (InterruptedException e) {e.printStackTrace();}
}4.3 读写锁
4.3.1 读写锁的概念
读写锁Read-Write Lock是一种允许多个读操作同时进行但写操作必须独占的锁机制。读写锁分为两种锁读锁和写锁。
读锁允许多个线程同时获取读锁只要没有线程持有写锁。读锁之间是共享的。写锁只允许一个线程获取写锁并且在写锁持有期间其他线程不能获取读锁或写锁。写锁是独占的。
读写锁的主要目的是提高并发性和性能。在读多写少的场景下读写锁可以显著提高系统的并发处理能力。
4.3.2 读写锁的实现原理
读写锁的实现通常依赖于两个锁一个读锁和一个写锁。读锁允许多个线程同时获取而写锁只允许一个线程获取。在获取写锁时需要确保没有线程持有读锁或写锁。
在Redisson中读写锁的实现基于Redis的原子操作和Lua脚本。Redisson通过两个键来分别控制读锁和写锁并使用Lua脚本确保锁操作的原子性。
4.3.3 简单使用
public void performTaskWithReadWriteLock() {// 1. 获取读写锁对象RReadWriteLock readWriteLock redissonClient.getReadWriteLock(myReadWriteLock);// 2. 从读写锁对象中分别获取读锁和写锁RLock readLock readWriteLock.readLock();RLock writeLock readWriteLock.writeLock();try {// 3. 尝试获取读锁if (readLock.tryLock(10, 60, TimeUnit.SECONDS)) { try {System.out.println(获取读锁正在执行读任务...);// 执行读任务} finally {// 4. 释放读锁readLock.unlock(); System.out.println(释放读锁。);}}// 5. 尝试获取写锁if (writeLock.tryLock(10, 60, TimeUnit.SECONDS)) { try {System.out.println(获取写锁正在执行写任务...);// 执行写任务} finally {// 6. 释放写锁writeLock.unlock();System.out.println(释放写锁。);}}} catch (InterruptedException e) {e.printStackTrace();}
}4.4 联锁
4.4.1 联锁的概念
联锁MultiLock是一种允许将多个锁关联在一起实现“全部获取”或“全部释放”的锁机制。
全部获取: 只有当所有参与联锁的锁都被成功获取后才算成功获取联锁。全部释放: 释放联锁时会自动释放所有参与联锁的锁。
联锁适用于需要同时获取多个资源的场景例如分布式事务中需要锁定多个数据表。
4.4.2 联锁的实现原理
Redisson 的联锁基于 RedissonMultiLock 对象实现。RedissonMultiLock 对象可以将多个 RLock 对象关联在一起并提供 tryLock() 和 unlock() 方法来统一管理这些锁。
在调用 tryLock() 方法时RedissonMultiLock 会尝试依次获取所有参与联锁的锁。如果所有锁都获取成功则返回 true否则释放已经获取到的锁并返回 false。
在调用 unlock() 方法时RedissonMultiLock 会自动释放所有参与联锁的锁无论这些锁是否被当前线程持有。
4.4.3 简单使用
public void performTaskWithMultiLock() {// 获取多个锁对象RLock lock1 redissonClient.getLock(lock1);RLock lock2 redissonClient.getLock(lock2);RLock lock3 redissonClient.getLock(lock3);// 创建联锁对象RLock multiLock redissonClient.getMultiLock(lock1, lock2, lock3);try {// 尝试获取联锁等待 10 秒if (multiLock.tryLock(10, TimeUnit.SECONDS)) {try {System.out.println(获取联锁成功正在执行任务...);// 执行需要所有锁的任务} finally {// 释放联锁multiLock.unlock();System.out.println(释放联锁。);}} else {System.out.println(获取联锁失败。);}} catch (InterruptedException e) {e.printStackTrace();}
}代码分析 获取多个锁对象: 首先获取需要参与联锁的多个 RLock 对象。创建联锁对象: 使用 redissonClient.getMultiLock(lock1, lock2, lock3) 创建一个 RLock 对象并将之前获取的多个锁对象作为参数传入。尝试获取联锁: 调用 multiLock.tryLock(10, TimeUnit.SECONDS) 尝试获取联锁最多等待 10 秒。执行任务: 如果成功获取联锁则执行需要所有锁保护的任务。释放联锁: 最后在 finally 块中调用 multiLock.unlock() 释放联锁这会自动释放所有参与联锁的锁。 5. WatchDog机制
想象一下我们正在进行一场激烈的拔河比赛。我们队好不容易抓住了绳子眼看就要赢了结果突然有人手滑绳子就被对方抢走了
在分布式系统中获取锁就好像抓住这根拔河绳。Redisson 分布式锁的租约时间就好像我们抓住绳子的时间。如果在租约时间内我们没有完成任务锁就自动释放了其他线程就有机会获取锁这就像拔河比赛中我们手滑绳子被抢走一样可能会导致数据不一致的问题。
为了避免这种情况发生Redisson 提供了 Watch Dog 机制就像我们队伍里安排了一个“观察员”。这位观察员会每隔一段时间关注我们是否还抓着绳子如果发现我们快坚持不住了就会及时提醒我们让我们重新握紧绳子并延长我们抓住绳子的时间。
具体来说Redisson 的Watch Dog 机制会在 Redisson 实例被关闭前不断的延长锁的有效期也就是说如果一个拿到锁的线程一直没有完成逻辑那么看门狗会帮助线程不断的延长锁超时时间锁不会因为超时而被释放。
**开启方式**在获取锁的时候不能指定leaseTime或者只能将leaseTime设置为-1这样才能开启看门狗机制。
public void test() throws Exception {RLock lock redissonClient.getLock(myLock);// 方式一: 不停重试直到获取锁成功具有 Watch Dog 自动延期机制默认续约时间为 30 秒lock.lock(); // 方式二: 尝试获取锁 10 秒获取成功返回 true否则返回 false具有 Watch Dog 自动延期机制默认续约时间为 30 秒boolean res1 lock.tryLock(10, TimeUnit.SECONDS); // 方式三: 尝试获取锁 10 秒如果获取成功则持有锁否则抛出异常leaseTime 为 10 秒不会自动续约try {lock.lock(10, TimeUnit.SECONDS); } catch (InterruptedException e) {// 处理异常}// 方式四: 尝试获取锁 100 秒如果获取成功则持有锁 10 秒leaseTime 为 10 秒不会自动续约boolean res2 lock.tryLock(100, 10, TimeUnit.SECONDS); Thread.sleep(40000L);lock.unlock();
}6. 总结
在本文中我们简要介绍了Redisson及其优势介绍了如何在Spring Boot项目中集成Redisson。通过代码示例展示了基本的分布式锁用法以及高级用法如公平锁、可重入锁、读写锁和联锁。除此之外我们还简要介绍了Redisson 的Watch Dog 机制希望本文对大家有所帮助。