Redisson 实现分布式锁简单解析
目录
- Redisson 实现分布式锁
- 业务方法:
- 加锁逻辑
- LockUtil 工具类
- 锁余额方法:
- 工具类代码
- 枚举代码
- RedisUtil 工具类
- tryLock 方法及重载【分布式锁具体实现】
- Supplier 函数式接口调用分析
Redisson 实现分布式锁
业务方法:
如图,简单的执行一个增加余额的方法,为保证数据一致性,那么可以通过Redisson分布式锁来实现。
加锁逻辑
LockUtil 工具类
锁余额方法:
工具类代码
/**
* @author lujinhong
* @since 2025/3/25
*/
public class LockUtil {
/**
* 锁商家余额
*
* @param business 商家id
* @param supplier 函数式接口;代表一个无参但有返回值的函数,就是代表加锁后要执行的业务逻辑
* @param lockFail 加锁失败后执行的自定义业务逻辑,这里就是一个简单的抛异常
*/
public static <T> T businessBalance(Integer business, Supplier<T> supplier, InvokeInter lockFail) {
return tryLock(RedisKey.LOCK_BUSINESS_BALANCE.getKey(business), supplier, lockFail);
}
}
枚举代码
/**
* @author lujinhong
* @since 2025/3/25
*/
@Getter
@AllArgsConstructor
public enum RedisKey {
// 商家余额
LOCK_BUSINESS_BALANCE("lock_business_balance_");
private final String key;
public String getKey(Number number) {
return key + number;
}
}
RedisUtil 工具类
tryLock 方法及重载【分布式锁具体实现】
/**
* @author lujinhong
* @since 2025/3/25
*/
@Slf4j
@Component
public class RedisUtil {
public static RedisUtil _this;
@Resource
private RedissonClient redissonClient;
@PostConstruct
public void init() {
_this = this;
}
/**
* 设置分布式锁
*
* @param key 就是上面用商家id生成的一个key
* @param supplier 函数式接口,加锁后要执行的业务逻辑
* @param lockFail 加锁失败后执行的业务逻辑:这里是抛异常
*/
public static <T> T tryLock(String key, Supplier<T> supplier, InvokeInter lockFail) {
return tryLock(key, supplier, lockFail, 10, TimeUnit.SECONDS);
}
/**
* 设置分布式锁--具体实现
* <p>
* 注意事项1:.getLock(key):用商家Id作为key生成一个rLock对象,底层代码是通过new来创建实例的,也就是说不同线程操作同一个商家获取到的是不同的RLock实例,
* -----------重点来了:虽然 RLock 实例在 Java 层是不同的,但是它们 指向同一个 Redis 锁,这个锁的标识是由 key(比如 LOCK_BUSINESS_BALANCE:1001)决定的。
* --------------------所以才需要在加锁之前,判断当前这个锁实例是否被其他线程占用了等判断--> if (lock.isLocked() && !lock.isHeldByCurrentThread())
* <p>
* 锁实例:RLock 是一个 Java 对象,代表分布式锁,它操作 Redis 中的实际锁资源。
* 锁资源:是 Redis 中的一个 key,用于标识是否已被某个线程持有,实际上是分布式锁的底层实现。
* <p>
* 获取锁,其实就是当前线程获得了对某个共享资源的独占访问权限
* <p>
* lock.isHeldByCurrentThread() 的底层实现依赖于 Redis 中 锁的 value 存储了持有锁的线程的 ID。该方法通过查询 Redis 锁的 value,并与当前线程的 ID 比较,来判断当前线程是否持有该锁
*
* @param key 用来生成 RLock 锁实例的key
* @param supplier 函数式接口,在加锁成功后执行的业务逻辑
* @param lockFail 加锁失败后执行的业务逻辑
* @param amount 当前线程在获取锁时最多等待的时间【注意是获取锁的时间,不是获取到该锁后锁的存活时间】,就是给你多少时间去获取锁
* @param unit 时间单位
*/
public static <T> T tryLock(String key, Supplier<T> supplier, InvokeInter lockFail, long amount, TimeUnit unit) {
// 创建一个 redisson 分布式锁的锁实例:
// 这个实例并不是直接获取Redis里的锁,而是一个Java对象,它是 Redisson 对 Redis 分布式锁的封装,用于后续操作:
// 如 加锁tryLock()、释放锁unlock()、查询锁状态 isLocked()
RLock lock = _this.redissonClient.getLock(key);
try {
// lock.isLocked():判断的是 Redis 中该 key(锁资源)是否已经被其他线程或进程获取。
// !lock.isHeldByCurrentThread():判断当前线程是否持有该锁,就是通过 Redis 中的 key 来检查是否是当前线程获得了该锁
if (lock.isLocked() && !lock.isHeldByCurrentThread()) {
if (lockFail == null) {
// 直接抛异常
throw BizException.newInstance(ErrorCode.BUSY);
}
// 如果有自定义的加锁失败逻辑,则执行
lockFail.invoke();
}
// 真正的加锁:在指定时间(amount)内获取锁,获取不到则返回false;
// 问题:lock.tryLock获取到锁之后,redis内部是做了什么操作:其实就是操作了一句 setNx 命令而已。
else if (lock.tryLock(amount, unit)) {
try {
// 执行具体的业务逻辑
return supplier.get();
} finally {
// 释放锁
lock.unlock();
}
} else {
// 上面没获取到锁,则报错
if (lockFail == null) {
// 默认就抛这个异常
throw BizException.newInstance(ErrorCode.BUSY);
}
// 如果有自定义的异常处理,则执行
lockFail.invoke();
}
return null;
} catch (InterruptedException e) {
throw BizException.newInstance(ErrorCode.BUSY);
}
}
}