组件讲解-分布式锁使用全攻略,轻松掌握并发控制的利器
分布式锁在微服务中应用的非常的广泛,网上也有很多关于分布式锁的demo,但绝大部分应用到实际项目中还是不够完善,各种细节问题考虑的不够到位
本项目中的分布式锁是经过了长时间的生产环境验证,配置也非常的灵活,对于细节的问题也进行了处理
此分布锁是在经典的Redisson开源项目基础上,再次完善的封装,使用了**自定义注解 + Spring的AOP的方式,**使用起来特别的简单便利,另外也提供了方法级别
为什么使用AOP,而不直接使用方法级别?
目前项目的结构都是采用三层结构,控制层 + service层 + 持久层
- 控制层 负责将service方法返回,充当前端接口调用
- service层 复杂复杂逻辑逻辑的编写
- 持久层 提供对数据库的操作和sql的编写,比如Mapper
使用自定义注解+切面来实现分布式锁,如果采用方法级别Lock.lock的方式,如果存在事务的话需要考虑是否包含事务的问题,如果方法嵌套过多的话,对事务原理不熟悉的开发人员可能会保证不了原子性。不过可以放在Controller层解决,但这么做属于层级混乱,controller就是控制层,不应有过多的逻辑。或者设计出加锁的一层,介于Controlle层和Service层之间
关于此问题的详细讲解,可跳转到相关文档查看
下面我们来开始正式的讲解分布式锁组件
分布式锁组件
依赖
xml
<dependency>
<groupId>com.example</groupId>
<artifactId>damai-service-lock-framework</artifactId>
<version>${revision}</version>
</dependency>实现的特点
- 将注解**@ServiceLock **** **加在方法上即可,锁的键可以根据入参的多个参数一起配置,这样比较灵活,如果使用用户Token的话锁的粒度太大
- 锁的类型包含可重入锁(默认)、公平锁、读锁、写锁
- 可配置锁的等待时间
- 提供了加锁失败后直接拒绝(默认)和自定义处理的两种处理方式
使用
- 在Springboot配置文件上配置好redis的地址
- 然后在相应的方法上添加
@ServiceLock
java
@ServiceLock(name = LOCK_DATA,keys = {"#lockDataDto.id"},waitTime = 5L)
public void addServiceLockStock(LockDataDto lockDataDto)@ServiceLock注解属性
| 属性值 | 类型 | 可否默认 | 含义 | 备注 |
|---|---|---|---|---|
| lockType | LockType | Y | 锁的类型 | Reentrant 可重入锁(默认)、Fair公平锁、Read读锁、Write写锁 |
| name | String | Y | 锁的业务名 | 如:order |
| keys | String[] | N | 锁的唯一标识 | 可指定多个,并支持SpEL表达式,如 |
| waitTime | long | Y | 尝试加锁最多等待时间 | 默认10s |
| timeUnit | TimeUnit | Y | 时间单位 | 默认秒 |
| lockTimeoutStrategy | LockTimeOutStrategy | Y | 加锁超时的处理策略 | 默认快速失败 |
| customLockTimeoutStrategy | String | Y | 自定义加锁超时的处理策略 | 此属性填写自定义处理策略方法名(入参和出参保持和加锁方法一致),如果为空则为快速拒绝策略 |
方法级别
除了提供AOP的加锁方式外,也提供了 方法级别 的操作,来满足开发者的各种业务需求
ServiceLockTool
java
/**
* 没有返回值的加锁执行
* @param taskRun 要执行的任务
* @param name 锁的业务名
* @param keys 锁的标识
*
* */
public void execute(TaskRun taskRun,String name,String [] keys)
/**
* 没有返回值的加锁执行
* @param taskRun 要执行的任务
* @param name 锁的业务名
* @param keys 锁的标识
* @param waitTime 等待时间
*
* */
public void execute(TaskRun taskRun,String name,String [] keys,long waitTime)
/**
* 没有返回值的加锁执行
* @param lockType 锁类型
* @param taskRun 要执行的任务
* @param name 锁的业务名
* @param keys 锁的标识
*
* */
public void execute(LockType lockType,TaskRun taskRun,String name,String [] keys)
/**
* 没有返回值的加锁执行
* @param lockType 锁类型
* @param taskRun 要执行的任务
* @param name 锁的业务名
* @param keys 锁的标识
* @param waitTime 等待时间
*
* */
public void execute(LockType lockType,TaskRun taskRun,String name,String [] keys,long waitTime)
/**
* 有返回值的加锁执行
* @param taskCall 要执行的任务
* @param name 锁的业务名
* @param keys 锁的标识
* @return 要执行的任务的返回值
* */
public <T> T submit(TaskCall<T> taskCall,String name,String [] keys)
/**
* 获得锁
* @param lockType 锁类型
* @param name 锁的业务名
* @param keys 锁的标识
*
* */
public RLock getLock(LockType lockType, String name, String [] keys)
/**
* 获得锁
* @param lockType 锁类型
* @param lockName 锁名
*
* */
public RLock getLock(LockType lockType, String lockName)示例1
这种锁是使用命令模式加锁,加锁逻辑包装成TaskRun接口,传入execute方法,类似于线程池的提交
java
public void testLock(String name,long id){
serviceLockTool.execute(() -> updateData(name,id),LOCK_DATA, new String[]{String.valueOf(id)});
}
public void updateData(String name,long id){
//模拟修改数据
}示例2
java
public void testLock(String name,long id){
RLock lock = serviceLockTool.getLock(LockType.Reentrant, LOCK_DATA, new String[]{String.valueOf(id)});
lock.lock();
try {
updateData(name,id);
}finally {
lock.unlock();
}
}
public void updateData(String name,long id){
//模拟修改数据
}以上就是关于分布式锁的使用,关于详细设计的讲解部分,可跳转到相关文档
更新: 2026-03-09 09:24:42
原文: https://www.yuque.com/u22210564/ykdrdh/nvs9pa10gomg6ela