基于setnx实现分布式锁存在下面问题
1.锁不可重入
同一个线程无法多次获取同一把锁。
2.不可重试
上一篇中实现的分布式锁是非阻塞式的,如果获取锁失败就立刻返回获取锁失败,不会重试获取锁。没有重试机制。
3.锁超时释放
虽然锁超时释放可以避免死锁,但如果是业务本身执行耗时较长,也会导致锁释放,存在安全隐患。
4.主从一致
如果redis是主从集群的,主从同步存在延迟,当主宕机时还没来得及方锁数据同步给从节点,那么其他线程就获取到了锁,造成线程安全问题。
Redisson
Redisson是一个基于redis基础上实现的java驻内存数据网格。
提供了一系列分布式的java常用对象。
提供了许多分布式服务。其中就包括分布式锁。
java
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.6</version>
</dependency>
java
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 {
@Bean
public 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);
}
}
java
/**解决方案:使用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.返回订单id
return voucherOrder.getId();
} finally {
//释放锁
lock.unlock();
}
redisson可重入锁的原理
key为锁名称,value为hash结构分别存入线程标识 和 锁可重入的次数,当可重入次数减为0的时候,锁才会被删除。

