- 1.service层代码
@Transactional
@Override
public void stopAll(Long appId) {
// 获取锁
String lockKey = STOP_RUN_LOCK_PRE + appId;
while (!lockService.lock(lockKey)) {
// 获取锁的过程可能操作数据库的次数太多,后期优化
}
log.info(lockKey+"上锁:"+ Thread.currentThread().getName());
try {
//----------------------业务代码
} finally {
// 释放锁
lockService.unLock(lockKey);
log.info(lockKey+"释放锁:"+ Thread.currentThread().getName());
}
}
- 2.上锁和解锁代码
/**
* 上锁(上锁成功返回true)
* @param key
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public Boolean lock(String key) {
Lock lock = new Lock();
lock.setLockKey(key).setCreateTime(new Date());
// 上锁成功返回true 失败返回false
try {
return this.save(lock);
}catch (Exception e){
return false;
}
}
/**
* 释放锁(有可能释放返回的是false,代表释放的锁不存在)
* @param key
*/
@Override
public Boolean unLock(String key) {
Lock lock = new Lock();
lock.setLockKey(key);
return this.removeById(key);
}
- 3.定时清理超时锁的代码
/**
* 清楚超时锁(清除超时30分钟的锁)
*/
@Override
public void cleanLock() {
LambdaQueryWrapper<Lock> tWrapper = new LambdaQueryWrapper<>();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE,-5);
tWrapper.lt(Lock::getCreateTime, calendar.getTime());
this.remove(tWrapper);
}
- 4.事情大概描述:图片代码块中的业务代码发生报错,然后发生回滚,但锁没有释放。
- 5.首先注意三个地方,service层方法加了@Transactional注解,加锁的方法加了@Transactional(propagation = Propagation.REQUIRES_NEW),解锁的方法没有使用到事务。也就是说当service层的业务代码发生报错,加锁方法是不会回滚的(这里可以去了解一下spring的事务传播机制),而解锁的方法是会跟着回滚。
- 6.回滚的经过:这里有大神可能会注意到finally里面执行了解锁,无论什么报错锁最后都会释放。这里解释一下,spring的事务是切面的,会在方法执行完后再进行回滚。
graph TD
业务代码报错 --> 执行finally代码块解锁 --> spring事务回滚导致解锁操作也被回滚
7.定时解锁这里我个人觉得是有点鸡肋的,等30分钟解锁,然后下一个请求进来报错,又要等30分钟。。。。