项目场景:
某日,公司系统管理人员在使用系统在提交表单功能时,短时间内多次触发提交(前端没有做好防抖),日志系统记录到报错如下
java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: b2a8377a-20b0-4f98-b1ee-31b527c35873 thread-id: 60321 at org.redisson.RedissonBaseLock.lambda$unlockAsync0$2(RedissonBaseLock.java:290)
通过字面上意思可以得知是redisson在进行解锁的时候,该锁是所有权是被其他线上所持有,故而引发该异常
Redisson版本: 3.21.1
具体代码
贴个删减修改后的代码
@GetMapping("test")
public void test(){
RLock lock = redissonClient.getLock("88888");
try {
if (lock.isLocked()) {
throw new ServiceException("Service is locked");
}
lock.tryLock(1000, 50, TimeUnit.SECONDS);
Thread.sleep(20 * 1000);
} catch (Exception e) {
log.info("中断异常");
} finally {
if (lock.isLocked()) {
lock.unlock();
}
}
}
原因分析:
问题就在于lock.unlock();这里,从网上搜索到很多教学或者博客都是这样写的,
如 https://zhoutianyu.blog.csdn.net/article/details/104902469

问题复现:通过两次快速的接口调用,复现问题

这种写法无疑在高并发情况下会触发IllegalMonitorStateException,通过查看源码能看到,解锁的时候有获取当前线程进行判断(RedissonBaseLock)
@Override
public RFuture<Void> unlockAsync(long threadId) {
return getServiceManager().execute(() -> unlockAsync0(threadId));
}
private RFuture<Void> unlockAsync0(long threadId) {
CompletionStage<Boolean> future = unlockInnerAsync(threadId);
CompletionStage<Void> f = future.handle((opStatus, e) -> {
cancelExpirationRenewal(threadId);
if (e != null) {
if (e instanceof CompletionException) {
throw (CompletionException) e;
}
throw new CompletionException(e);
}
if (opStatus == null) {
IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
+ id + " thread-id: " + threadId);
throw new CompletionException(cause);
}
return null;
});
return new CompletableFutureWrapper<>(f);
}
解决方案:
正确的写法应该是在解锁之前增加判断是否当前线程持有的锁,lock.isHeldByCurrentThread()
@GetMapping("test")
public void test(){
RLock lock = redissonClient.getLock("88888");
try {
if (lock.isLocked()) {
throw new ServiceException("Service is locked");
}
lock.tryLock(1000, 50, TimeUnit.SECONDS);
Thread.sleep(20 * 1000);
} catch (Exception e) {
log.info("中断异常");
} finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
总结
网上有很多知识存在年份过久,依赖版本更新等问题,在搬代码的同时需要多注意版本是否有更新。

1万+

被折叠的 条评论
为什么被折叠?



