Redisson分布式锁 | 您所在的位置:网站首页 › 什么叫飞行包线工艺工程师 › Redisson分布式锁 |
1. 解铃还须系铃人
1.1 锁执行超时后却还释放锁
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(),它的意思是查询当前线程是否持有此锁定,如果还持有,则释放,如果未持有,则说明已被释放; 1.2 线程并发时,线程B释放线程A的锁我需要先来描述一下这种场景: 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原因分析: 原因: 公司自研的缓存集群不支持publish命令; |
CopyRight 2018-2019 实验室设备网 版权所有 |