函数式接口实现分布式锁
背景
最近使用分布式锁比较多,发现大家使用分布式锁,都各有各的用法,想着用搞一个加锁模板,方便大家使用,就选择声明一个函数式接口,去实现加锁的模板
函数式接口
是一种特殊的接口,只包含一个抽象方法。函数式接口的目的是为了支持函数式编程,使开发者能够以更简洁的方式定义单一抽象方法的接口,从而可以使用Lambda表达式来实现这个接口的抽象方法。可以通过lambda表达式进行传递,简单理解就是java8对函数式编程的一种支持
实战
我们一般加锁是这样,尝试获取锁,失败则抛异常,成功则执行业务代码,每次都要写一大段逻辑
RLock lock = redissonClient.getLock(lockKey);
try {
if (!lock.tryLock(waitTime, leaseTime, unit)) {
throw new CommonException(ErrorCodeEnum.TRY_LOCK_ERROR).detailMessage("操作频繁,请稍后再试!");
}
。。。。业务代码
}catch (CommonException commonException) {
} catch (Exception e) {
log.error("LockTemplate >> 获取锁异常",e);
} finally {
try {
// 锁不为空 是否还是锁定状态 当前执行线程的锁
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
} catch (Exception e) {
log.error("LockTemplate >> 获取锁异常",e);
}
}
}
所以可以采用函数是接口,定义一套加锁模板,开发者就可以只关注业务代码,而不用去写一大段加锁逻辑,
package com.dept.common.template;
import cn.hutool.core.util.StrUtil;
import com.dept.common.enums.ErrorCodeEnum;
import com.dept.common.exception.CommonException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
@AllArgsConstructor
@Slf4j
public class LockTemplate {
private RedissonClient redissonClient;
public void templateLock(String prefix,String key,Runnable target,long waitTime, long leaseTime, TimeUnit unit) {
String lockKey = StrUtil.format(prefix,key);
// 判断是否正在使用
RLock lock = redissonClient.getLock(lockKey);
try {
if (!lock.tryLock(waitTime, leaseTime, unit)) {
throw new CommonException(ErrorCodeEnum.TRY_LOCK_ERROR).detailMessage("操作频繁,请稍后再试!");
}
// 执行业务代码
target.run();
}catch (CommonException e){
throw e;
} catch (Exception e) {
log.error("LockTemplate >> 获取锁异常",e);
} finally {
try {
// 锁不为空 是否还是锁定状态 当前执行线程的锁
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
} catch (Exception e) {
log.error("LockTemplate >> 获取锁异常",e);
}
}
}
}
重点就是将Runnable做为入参,Runnable是一个函数式接口,所以我们可以直接传入业务实现,当然我们也可以自己定义一个函数式接口
lockTemplate.templateLock(SystemConstants.STAR_TASK_MINA_ORDER_LOCK, order.getOrderSn(), () -> {
。。。业务实现
}, RedisConstants.ONE_SECOND, RedisConstants.TEN_SECOND, TimeUnit.MINUTES);
这样我们就可以不用去关注加锁逻辑,只需要写自己的业务代码接口
总结
通过函数式接口,我们可以定义很多模板代码,让开发者只需要传入对应的业务实现即可,TransactionTemplate的execute的方法也是类似的实现