Caused by: java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 32caba49-5799-491b-aa7b-47d789dbca93 thread-id: 1
先来看一段代码:
@Test
public void testSimpleLock2() {
String key = "srm-sync-test-lock";
try {
// 获取锁,持有8秒后,自动释放
tryLock(key, 1000L, 8000L);
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
e.printStackTrace();
} finally {
unlock(key);
}
}
public boolean tryLock(String lockKey, long waitTime, long leaseTime) {
boolean suc;
try {
RLock lock = redissonClient.getLock(lockKey);
// 第一个参数是等待时间,n毫秒内获取不到锁,则直接返回
// 第二个参数 m毫秒后强制释放锁
suc = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
} catch (Throwable e) {
String msg = String.format("LOCK FAILED: key=%s||tryLockTime=%s||lockExpiredTime=%s", lockKey, waitTime, leaseTime);
throw new IllegalStateException(msg, e);
}
return suc;
}
public void unlock(String lockKey) {
try {
RLock lock = redissonClient.getLock(lockKey);
if (lock != null) {
lock.unlock();
}
} catch (Throwable e) {
String msg = String.format("UNLOCK FAILED: key=%s", lockKey);
throw new IllegalStateException(msg, e);
}
}
这块执行完之后,报错:
Caused by: java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 32caba49-5799-491b-aa7b-47d789dbca93 thread-id: 1
at org.redisson.RedissonLock.lambda$unlockAsync$3(RedissonLock.java:581)
at org.redisson.misc.RedissonPromise.lambda$onComplete$0(RedissonPromise.java:187)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:570)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:549)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:490)
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615)
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104)
at org.redisson.misc.RedissonPromise.trySuccess(RedissonPromise.java:82)
at org.redisson.command.RedisExecutor.handleReference(RedisExecutor.java:481)
at org.redisson.command.RedisExecutor.handleSuccess(RedisExecutor.java:474)
at org.redisson.command.RedisExecutor.handleResult(RedisExecutor.java:459)
at org.redisson.command.RedisExecutor.checkAttemptPromise(RedisExecutor.java:445)
at org.redisson.command.RedisExecutor.lambda$execute$3(RedisExecutor.java:163)
at org.redisson.misc.RedissonPromise.lambda$onComplete$0(RedissonPromise.java:187)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:570)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:549)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:490)
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615)
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104)
at org.redisson.misc.RedissonPromise.trySuccess(RedissonPromise.java:82)
at org.redisson.client.handler.CommandDecoder.completeResponse(CommandDecoder.java:444)
at org.redisson.client.handler.CommandDecoder.handleResult(CommandDecoder.java:439)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:370)
at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:196)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:134)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:104)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:498)
at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
原因分析:
获取锁成功之后,持有的时间是8秒,而如果你的锁内业务执行时间超过8秒后,锁会自动释放,锁自动释放后,而在你的finally里面又手动去释放锁,就导致了这个错误;
解决方法:
public void unlock(String lockKey) {
try {
RLock lock = redissonClient.getLock(lockKey);
if (lock != null && lock.isHeldByCurrentThread()) {
lock.unlock();
}
} catch (Throwable e) {
String msg = String.format("UNLOCK FAILED: key=%s", lockKey);
throw new IllegalStateException(msg, e);
}
}
解锁时,加了lock.isHeldByCurrentThread(),它的意思是查询当前线程是否持有此锁定,如果还持有,则释放,如果未持有,则说明已被释放;
我需要先来描述一下这种场景:
tryLock(key, 1000L, 8000L); 如果线程A获取到锁之后,此时线程B也来获取锁,从此行代码可看出,最多等待1秒,如果1秒后未获取到锁,会执行锁内的代码吗?
@Test
public void testConcurrentLock() {
String key = "srm-sync-test-lock";
LockThread lockThreadA = new LockThread(key, 5);
LockThread lockThreadB = new LockThread(key, 1);
try {
new Thread(lockThreadA, "Thread-A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(lockThreadB, "Thread-B").start();
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public boolean tryLock(String lockKey, long waitTime, long leaseTime) {
boolean suc;
try {
RLock lock = redissonClient.getLock(lockKey);
suc = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);
} catch (Throwable e) {
String msg = String.format("LOCK FAILED: key=%s||tryLockTime=%s||lockExpiredTime=%s", lockKey, waitTime, leaseTime);
throw new IllegalStateException(msg, e);
}
return suc;
}
public void unlock(String lockKey) {
try {
RLock lock = redissonClient.getLock(lockKey);
if (lock != null) {
lock.unlock();
}
} catch (Throwable e) {
String msg = String.format("UNLOCK FAILED: key=%s", lockKey);
throw new IllegalStateException(msg, e);
}
}
class LockThread implements Runnable {
private String lockKey;
private int sleepTime;
public LockThread(String lockKey, int sleepTime) {
this.lockKey = lockKey;
this.sleepTime = sleepTime;
}
@Override
public void run() {
try {
if (tryLock(lockKey, 1000L, 8000L)) {
TimeUnit.SECONDS.sleep(sleepTime);
System.out.println("===============" + Thread.currentThread().getName());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
unlock(lockKey);
}
}
}
输出结果:
Caused by: java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 9b42c695-220d-4194-a020-131309ebcd87 thread-id: 56
at org.redisson.RedissonLock.lambda$unlockAsync$3(RedissonLock.java:581)
at org.redisson.misc.RedissonPromise.lambda$onComplete$0(RedissonPromise.java:187)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:570)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:549)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:490)
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615)
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104)
at org.redisson.misc.RedissonPromise.trySuccess(RedissonPromise.java:82)
at org.redisson.command.RedisExecutor.handleReference(RedisExecutor.java:481)
at org.redisson.command.RedisExecutor.handleSuccess(RedisExecutor.java:474)
at org.redisson.command.RedisExecutor.handleResult(RedisExecutor.java:459)
at org.redisson.command.RedisExecutor.checkAttemptPromise(RedisExecutor.java:445)
at org.redisson.command.RedisExecutor.lambda$execute$3(RedisExecutor.java:163)
at org.redisson.misc.RedissonPromise.lambda$onComplete$0(RedissonPromise.java:187)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:570)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:549)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:490)
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615)
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:604)
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104)
at org.redisson.misc.RedissonPromise.trySuccess(RedissonPromise.java:82)
at org.redisson.client.handler.CommandDecoder.completeResponse(CommandDecoder.java:444)
at org.redisson.client.handler.CommandDecoder.handleResult(CommandDecoder.java:439)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:370)
at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:196)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:134)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:104)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:498)
at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
... 1 more
===============Thread-A
原因分析:
解决办法: 和上面的案例一的解决办法相同,在释放锁时,加上lock.isHeldByCurrentThread()的判断,虽然线程B没有获取到锁,也没有执行锁内的业务,但是,它一定执行finally里面的unlock方法;
Caused by: org.redisson.client.RedisException: ERR running script (call to f_3ffe249c16dee540ac8ab32e39bd408626b9aff7): @user_script:1: ERR fusion r2 unknown cmd [publish. channel: [id: 0x5e3c4063, L:/172.30.39.133:55920 - R:10.179.133.91/10.179.133.91:3303] command: (EVAL), params: [if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then return nil;end; local counter = redis.call('h..., 2, lock_key, redisson_lock__channel:{lock_key}, 0, 30000, 6494e66f-11c1-45f5-821f-52b33275b11a:55]
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:355)
at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:196)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:134)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:104)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:498)
at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:355)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:377)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
... 1 more
原因:
公司自研的缓存集群不支持publish命令;