一、简要说明
此篇文章主要将项目中使用Redisson框架实现分布式锁的代码抽取出来,供大家参考.
二、前言
- 首先引入maven依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.6</version>
</dependency>
- 配置redis,如果项目中已经配置了redis则不需要进行任何改动.(集群redis或者主从redis需要额外配置,请自行百度!)
三、代码
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@Slf4j
@Component
public class DistributedLockUtils {
@Autowired
private RedissonClient redissonClient;
@FunctionalInterface
public interface LockTemplate {
/**
* 执行业务逻辑,无返回值
*/
void execute();
}
@FunctionalInterface
public interface LockResultTemplate<T> {
/**
* 执行业务代码,有返回值
*
* @return 业务代码的返回值
*/
T execute();
}
/**
* 内部使用,用于包装 {@link LockTemplate} 和{@link LockResultTemplate}
*
* @param <T>
*/
@FunctionalInterface
private interface LockOperation<T> {
/**
* 执行业务代码
*
* @return 有返回值的情况下返回操作的结果,无返回值的情况下返回null
*/
T execute();
}
/**
* 获取分布式锁并执行无返回值的操作。
*
* @param lockTemplate 无返回值的操作代码函数
* @param waitTime 获取锁的超时时间(单位:秒)
* @param lockName 锁的名称函数,用于获取锁的名称
*/
public void lock(LockTemplate lockTemplate, Integer waitTime, Supplier<String> lockName) {
// 将LockTemplate转换为LockOperation,执行操作
LockOperation<Void> lockOperation = () -> {
lockTemplate.execute();
return null;
};
// 调用通用的execute方法获取锁并执行操作
execute(lockOperation, waitTime, lockName.get());
}
/**
* 获取分布式锁并执行无返回值的操作。
* <p>
* 默认获取锁的超时时间为:10秒
*
* @param lockTemplate 无返回值的操作代码函数
* @param lockName 锁的名称函数,用于获取锁的名称
*/
public void lock(LockTemplate lockTemplate, Supplier<String> lockName) {
// 将LockTemplate转换为LockOperation,执行操作
LockOperation<Void> lockOperation = () -> {
lockTemplate.execute();
return null;
};
// 调用通用的execute方法获取锁并执行操作
execute(lockOperation, DistributedLockConstant.LOCK_WAIT_SECONDS, lockName.get());
}
/**
* 获取分布式锁并执行带有返回值的操作。
*
* @param lockResultTemplate 带有返回值的操作代码函数
* @param waitTime 获取锁的超时时间(单位:秒)
* @param lockName 锁的名称函数,用于获取锁的名称
* @param <T> 返回值的类型
* @return 带有返回值的操作的结果
*/
public <T> T lock(LockResultTemplate<T> lockResultTemplate, Integer waitTime, Supplier<String> lockName) {
// 将LockResultTemplate转换为LockOperation,执行操作
LockOperation<T> lockOperation = lockResultTemplate::execute;
// 调用通用的execute方法获取锁并执行操作,并返回结果
return execute(lockOperation, waitTime, lockName.get());
}
/**
* 获取分布式锁并执行带有返回值的操作。
* <p>
* 默认获取锁的超时时间为:10秒
*
* @param lockResultTemplate 带有返回值的操作代码函数
* @param lockName 锁的名称函数,用于获取锁的名称
* @param <S> 返回值的类型
* @return 带有返回值的操作的结果
*/
public <S> S lock(LockResultTemplate<S> lockResultTemplate, Supplier<String> lockName) {
// 将LockResultTemplate转换为LockOperation,执行操作
LockOperation<S> lockOperation = lockResultTemplate::execute;
// 调用通用的execute方法获取锁并执行操作,并返回结果
return execute(lockOperation, DistributedLockConstant.LOCK_WAIT_SECONDS, lockName.get());
}
/**
* 执行业务代码
*
* @param lockOperation 业务代码的具体实现逻辑
* @param time 获取锁的超时时间
* @param lockName 锁名称
*/
private <T> T execute(LockOperation<T> lockOperation, Integer time, String lockName) {
RLock lock = null;
try {
lock = redissonClient.getLock(lockName);
try {
boolean tryLock = lock.tryLock(time, TimeUnit.SECONDS);
if (!tryLock) {
log.error("===> RLock tryLock result false ,[lockName:{}]", lockName);
throw new UtilException("获取锁超时,请重试!");
}
//执行业务代码
return lockOperation.execute();
} catch (InterruptedException e) {
log.error("===> RLock tryLock InterruptedException,[lockName:{}]", lockName, e);
throw new UtilException("获取锁失败了,请重试!");
}
} finally {
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
//相关常量类
public class DistributedLockConstant {
/**
* 获取锁的超时时间(单位:秒)
*/
public static final Integer LOCK_WAIT_SECONDS = 10;
}
四、代码说明
- 使用了JAVA8的函数式接口将业务逻辑进行了封装
- 将redisson获取锁,执行业务代码,释放锁,这些代码进行了抽取,作为统一的方法进行调用
- 使用案例
@Service
public class TestServiceImpl{
@Autowired
private DistributedLockUtils lockUtils;
//无返回值的案例
public void test(){
lockUtils.lock(() -> {
log.info("业务代码执行了!");
}, () -> "kockKey");
}
//有返回值的案例
public void test01(){
String result = lockUtils.lock(() -> {
log.info("业务代码执行了!");
return "ok"
}, () -> "kockKey");
}
}