Redisson分布式锁 您所在的位置:网站首页 什么叫飞行包线工艺工程师 Redisson分布式锁

Redisson分布式锁

2024-03-09 05:45| 来源: 网络整理| 查看: 265

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

原因分析: 在这里插入图片描述 解决办法: 和上面的案例一的解决办法相同,在释放锁时,加上lock.isHeldByCurrentThread()的判断,虽然线程B没有获取到锁,也没有执行锁内的业务,但是,它一定执行finally里面的unlock方法;

2. 使用公司自研的redis加锁遇到的问题 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命令;



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有