一、代码
@ApiOperation(value = "报名用户取消报名")
@PutMapping("cancelAttend")
public Result<Boolean> cancelAttend(@RequestParam Long attendId) {
Long memberId = getMemberId();
RLock redissonLock = redissonClient.getLock("cancelAttend:" + attendId);
try {
redissonLock.lock(5, TimeUnit.SECONDS);
return Result.success(activityAttendService.cancelAttend(id, memberId));
} finally {
// 释放锁
if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) {
redissonLock.unlock();
}
}
}
二、注意事项
2.1 lock key说明
本例代码锁用的key不是直接用报名IDattendId
,而是在报名ID前加了一个字符串常量cancelAttend:
,即:"cancelAttend:" + attendId
。原因是可能还有其他方法也会用attendId
加锁,比如报名核销方法。如果取消报名、报名核销都用attendId做为lock key,就会出现两个方法共用一个key的情况,所以才在前面加了一个字符串常量来做区分。
2.2 释放锁
释放锁不是直接调用redissonLock.unlock();
,而是在释放锁之前加了一层判断。
redissonLock.isLocked()
:如果两个方法共用了一个lock key,就有出现key被第一个方法释放了,第二个方法又去释放,就会报错。redissonLock.isHeldByCurrentThread()
:在分布式服务中,就只需要释放当前线程加的锁。当然也需要加上redissonLock.isLocked()
。
2.3 事务问题
加锁代码不要写在@Transactional
事务里面,否则锁会失效,锁不住数据,还是会出现并发问题。可参考Redisson失效场景