问题现象
在并发操作的场景下(对业务接口连续请求三次),使用基于redis的分布式锁 RedissonLock解锁时抛出异常。
问题复现代码
public boolean testLock(Integer type) { RLock lock = redissonClient.getLock("testLock" + 22); log.info("testLock:1:" + lock.toString() + ",interrupted:" + Thread.currentThread().isInterrupted() + ",hold:" + lock.isHeldByCurrentThread() + ",threadId:" + Thread.currentThread().getId() + ",redissonClient:{}" + redissonClient); try { boolean res = lock.tryLock(3, 60, TimeUnit.SECONDS); log.info("testLock:2:" + lock.toString() + ",interrupted:" + Thread.currentThread().isInterrupted() + ",hold:" + lock.isHeldByCurrentThread() + ",threadId:" + Thread.currentThread().getId() + ",redissonClient:{}" + redissonClient + ",res:{}" + res); if (res) { // 业务代 Thread.sleep(4000L); } } catch (InterruptedException e) { log.error("分布式锁{}获取失败", lock.getName()); throw new BusinessProcessFailException("分布式锁【" + lock.getName() + "】获取失败", ErrorCode.SYSTEM_EXCEPTION.getCode()); } finally { log.info("testLock:3:" + lock.toString() + ",interrupted:" + Thread.currentThread().isInterrupted() + ",hold:" + lock.isHeldByCurrentThread() + ",threadId:" + Thread.currentThread().getId() + ",redissonClient:{}" + redissonClient); lock.unlock(); } return false; }
具体的异常信息如下
java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 1f24378c-5456-4321-827a-bc0a7515ec5d thread-id: 227
at org.redisson.RedissonLock.unlock(RedissonLock.java:366) ~[redisson-2.10.5.jar:na]
排查过程
业务代码中添加日志打印
log.info("testLock:1:" + lock.toString() // lock锁对象地址
+ ",interrupted:" + Thread.currentThread().isInterrupted() // 当前线程是否中断
+ ",hold:" + lock.isHeldByCurrentThread() // 当前请求的线程是否是锁对象的持有者
+ ",threadId:" + Thread.currentThread().getId() // 线程ID
+ ",redissonClient:{}" + redissonClient) // redissonClient地址
+ ",res:{}" + res); // 线程加锁结果
日志
- testLock:1:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:false,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80
- testLock:2:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true
- testLock:1:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80
- testLock:1:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:false,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80
- testLock:2:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80,res:{}false
- testLock:3:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80
- testLock:3:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80
- testLock:2:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true
- testLock:3:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80
通过日志数据可知
线程 242、235、244 使用的是同一个redissonClient
线程 242、235、244 使用的lock锁对象不是同一个,242是6132b8ee,235是33e44749,244是6da98d58
线程执行流程
testLock:1:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:false,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80 // 线程242构建锁对象
testLock:2:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true // 线程 242 加锁成功
testLock:1:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80 // 线程235构建锁对象
testLock:1:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:false,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80 // 线程 244 构建锁对象
testLock:2:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80,res:{}false // 线程235加锁失败
testLock:3:org.redisson.RedissonLock@33e44749,interrupted:false,hold:false,threadId:235,redissonClient:{}org.redisson.Redisson@51627e80 // 线程235 最终会走到finally 执行解锁,但是解锁失败(该线程并没有获取到锁)
testLock:3:org.redisson.RedissonLock@6132b8ee,interrupted:false,hold:true,threadId:242,redissonClient:{}org.redisson.Redisson@51627e80 // 线程 242 解锁成功
testLock:2:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80,res:{}true // 线程244加锁成功
testLock:3:org.redisson.RedissonLock@6da98d58,interrupted:false,hold:true,threadId:244,redissonClient:{}org.redisson.Redisson@51627e80 // 线程244解锁成功
解决方法
解锁时添加判断
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
lock.isLocked():判断要解锁的key是否已被锁定。
lock.isHeldByCurrentThread():判断要解锁的key是否被当前线程持有。
结论
1、只有加锁成功才需要解锁
2、自己加的锁,自己解,不能解别人的锁