27.Redisson基本使用和可重入性
基于setnx实现分布式锁存在下面问题
1.锁不可重入
同一个线程无法多次获取同一把锁。
2.不可重试
上一篇中实现的分布式锁是非阻塞式的,如果获取锁失败就立刻返回获取锁失败,不会重试获取锁。没有重试机制。
3.锁超时释放
虽然锁超时释放可以避免死锁,但如果是业务本身执行耗时较长,也会导致锁释放,存在安全隐患。
4.主从一致
如果redis是主从集群的,主从同步存在延迟,当主宕机时还没来得及方锁数据同步给从节点,那么其他线程就获取到了锁,造成线程安全问题。
Redisson
Redisson是一个基于redis基础上实现的java驻内存数据网格。
提供了一系列分布式的java常用对象。
提供了许多分布式服务。其中就包括分布式锁。
<!--redisson-->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version>
</dependency>
package com.xkj.org.config;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;/*** @author xiankejin* @descrition* @date 2025/10/15*/
@Configuration
public class RedisConfig {@Beanpublic RedissonClient redissonClient() {Config config = new Config();//userSingleServer表示设置单节点,如果有密码设置.setPassword()//如果是redis集群地址使用config.useClusterServers()添加集群地址config.useSingleServer().setAddress("redis://192.168.19.128:6379");//创建redisson客户端对象返回return Redisson.create(config);}}
/**解决方案:使用redisson */// 获取的锁是具有可重入性的,参数是指定锁的名称RLock lock = redissonClient.getLock("lock:order:" + userId);// 尝试获取锁,// 参数1:分别为获取锁的最大等待时间(期间会重试获取锁)可以不传,默认值为-1,表示获取锁失败立即返回,不等待。// 参数2,3:锁自动释放时间和单位,不传的情况,默认是30秒。// lock.tryLock(); 无参数的方法
// boolean isLock = lock.tryLock(1, 10, TimeUnit.SECONDS);boolean isLock = lock.tryLock();if (!isLock) {//获取锁失败throw new ServiceException("有个用户只允许购买一单,不允许重复下单");}try {//调用Service类自己的方法,使用代理对象,否则事务失效//1.需要引入aspectjweaver的依赖//2.springboot启动类上添加注解暴露代理对象IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();VoucherOrder voucherOrder = proxy.createVoucherOrder(voucherId, userId);//6.返回订单idreturn voucherOrder.getId();} finally {//释放锁lock.unlock();}
redisson可重入锁的原理
key为锁名称,value为hash结构分别存入线程标识和 锁可重入的次数,当可重入次数减为0的时候,锁才会被删除。