首页 > 数据库 >记 Redisson 报错 attempt to unlock lock, not locked by current thread

记 Redisson 报错 attempt to unlock lock, not locked by current thread

时间:2025-01-01 10:40:47浏览次数:1  
标签:释放 Redisson attempt thread java 52 报错 线程 id

原文:记一次 Redisson 线上问题 → 你怎么能释放别人的锁

错误信息:

attempt to unlock lock, not locked by current thread by node id: b9df1975-5595-42eb-beae-bdc5d67bce49 thread-id: 52

查看日志,找到对应的堆栈信息:

Exception in thread "thread0" java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: b9df1975-5595-42eb-beae-bdc5d67bce49 thread-id: 52
	at org.redisson.RedissonLock.lambda$unlockAsync$4(RedissonLock.java:616)
	at org.redisson.misc.RedissonPromise.lambda$onComplete$0(RedissonPromise.java:187)
	at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:578)
	at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:552)
	at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:491)
	at io.netty.util.concurrent.DefaultPromise.addListener(DefaultPromise.java:184)
	at org.redisson.misc.RedissonPromise.onComplete(RedissonPromise.java:181)
	at org.redisson.RedissonLock.unlockAsync(RedissonLock.java:607)
	at org.redisson.RedissonLock.unlock(RedissonLock.java:492)
	at com.qsl.ResissonTest.testLock(ResissonTest.java:41)
	at java.lang.Thread.run(Thread.java:748)

翻译过来就是:

企图去释放锁,不被当前线程(node id: b9df1975-5595-42eb-beae-bdc5d67bce49 thread-id: 52)锁住

也就是:当前线程企图去释放别的线程的锁。

怎么能释放别人的锁?

基础回顾

在排查问题之前,我们先弄清楚node id: b9df1975-5595-42eb-beae-bdc5d67bce49 thread-id: 52中的node idthread-id是什么。

关于thread-id,我相信大家都理解,就是抛异常的线程的 id,没问题吧?那node id呢?

回忆一下八股文:

问:redisson用的redis的什么数据类型来实现锁的

答:hash

问:那hash中的keyfieldvalue的值分别是什么

答:key的值是锁名,field的值是线程idvalue的值是重入次数

问:如果多个服务同时去获取一把锁,field的值是不是有可能相同,比如服务 A 获取锁的线程的thread-id是 52,服务 B 获取锁的线程的的thread-id也是 52

此时你是不是有点慌了,但依旧嘴硬的回答:有可能相同

问:那没问题吗,A 服务的线程(thread-id=52)拿到锁后,正在执行业务处理,B 服务的线程(thread-id=52)也能拿到锁,这不是锁了个寂寞?

答:呃...嗯...

很显然漏了个细节,那就是field,其值不是线程id,而是node id:thread-id,例如:b9df1975-5595-42eb-beae-bdc5d67bce49:52 ,而这个node id就是redisson实例id,用以区分分布式下的redisson实例。

具体来说,在创建RedissonClient时,会生成一个随机的node id,标识当前使用redisson的客户端。参考 Redisson 分布式锁实现之源码篇 → 为什么推荐用 Redisson 客户端

释放别人的锁

那为什么会释放别人的锁?看下面的代码:

testLock

过程如下:

  1. 构造锁
  2. 尝试获取锁,等待时间 1s,持锁 3s
  3. 如果获取到锁,则进行业务处理,没获取到锁,则打印锁获取失败
  4. finally保证异常和非异常情况下,锁都能释放

是不是很正常,但真的没bug吗?

我们调整下代码:

testLock_异常

运行multiThreadLock,异常就来了:

异常信息

从打印信息,我们应该能分析出问题出在哪:

  1. 线程 52 获取到锁,执行业务中
  2. 线程 53 尝试获取锁,但锁被线程 52 持有
  3. 线程 53 1s 内获取锁失败
  4. 线程 53 来到finally,判断锁是否被持有,发现是被持有的,释放锁
  5. redisson释放锁的时候,发现锁的持有线程并非当前线程,抛出异常

问题修复

既然找到问题了,修复问题就很简单了,方式有以下几种。

提高等待时长

将获取锁的等待时长提高,但这种方式只能减少异常,并不是完全修复异常;因为会有多个线程同时竞争锁,等待时长设置成多少都不合适,除非设置成不超时,但是设置成不超时,可能会导致等待的线程太多,造成线程不够用的情况。不推荐该方式。

自动释放

去掉finally,相当于把产生异常的源头给干掉了,那肯定就不会有异常了嘛。

解决不了问题,那就把提出问题的人解决掉

不主动释放锁,让锁自动到期释放,因为我们设置了锁持有时长是 3s,3s 后就自动到期释放了。但在实际业务中,我们往往会把锁持有时长设置的比较大(远大于业务执行的平均时长),保证业务不会并发执行,如果业务执行完了不主动释放锁,就会导致很长时间内锁被无效占用,后面的线程获取锁也只能白白等待。不推荐该方式。

记录获取状态

下面的代码尝试使用acquired变量记录当前线程是否获取到锁,释放锁的时候,判断是否加锁成功,如果加锁了,才释放锁。

记录获取状态_1

如果业务执行时间超过 3s,会怎么样,我们把睡眠时间改成 5s,执行下testLock,你会发现同样的异常又出现了!!!

记录获取状态_异常

我们来分析下,锁持有时长是 3s,而业务执行时长是 5s,也就说业务还没执行完,锁已到期,redis自动释放了,业务执行完之后我们再去释放锁,锁都没了,怎么释放?所以redisson抛出异常了;所以释放锁的时候,还需要加一个条件

if (acquired && lock.isLocked())

acquired表示当前线程是否获取到锁了,而lock.isLocked()表示是否有线程持有锁,如果都为true,那就说明是当前线程持有锁,释放是不是就没问题了?我们继续分析,假设第 4s 的时候,另外一个线程来获取锁,获取成功(因为当前线程的锁 3s 到期自动释放了嘛),第 5s 的时候,当前线程业务执行完成,来到 if 条件进行判断,当前线程的acquiredtrue(只能表示曾经拥有,而非此刻拥有),lock.isLocked()也为true(另外线程持有,而非当前线程持有),if 条件满足,去释放锁,结果可想而知,当前线程释放别的线程的锁,依旧会抛异常,而这个异常正是文章开头说到的那个异常。同样不推荐该方式。

判断持有者

这种写法更优雅:

判断持有者

就直接判断锁是不是当前线程持有,是就可以释放;就不用去管锁是别的线程持有,还是到期自动释放了。推荐该方式。

持有者释放

如下图所示:

开心一刻

锁不设置过期时间,由看门狗进行续期;当前线程获取锁之后,业务执行期间,锁一直被当前线程持有,业务执行完成之后,再由当前线程进行释放。谁持有谁释放,正是我们需要的,推荐该方式。

标签:释放,Redisson,attempt,thread,java,52,报错,线程,id
From: https://www.cnblogs.com/Higurashi-kagome/p/18645356

相关文章

  • centos/linux安装shellcheck报错
    centos/linux安装shellcheck报错在centos上安装shellcheck时,可能会因为各种各样的原因安装不了,如下:笔者在安装时使用命令sudoyuminstallshellcheck时常报错404,403等等还有就是笔者在用wget方式安装时也是不行,连不上github,显示连接超时这可让我犯愁了,要编写shell脚本需要......
  • 关于 qt qml 报错 QtQuick.Controls 2 没有被注册
    qml报错无法加载QtQuick.Controls背景一个简单的qtcreaterdemo,qml文件如下importQtQuick2.15importQtQuick.Window2.15Window{visible:truewidth:640height:480title:qsTr("HelloWorld")//创建一个红色的矩形Rectangle{......
  • 解决uniapp安卓打包targetSdkVersion报错
    解决GooglePlay版本检查问题的实用方案。Error:GooglePlayrequiresthatappstargetAPIlevel33orhigher.[ExpiredTargetSdkVersion]问题描述打包时遇到以下错误:Error:GooglePlayrequiresthatappstargetAPIlevel33orhigher.[ExpiredTargetSdkVersion]......
  • ScriptCS安装报错
    最近在写C#,在使用Chocolatey安装ScriptCS(命令:chocoinstallscriptcs)的过程中,出现以下报错:ERROR:Theterm'Write-ChocolateyFailure'isnotrecognizedasthenameofacmdlet,function,scriptfile,oroperableprogram.Checkthespellingofthename,orifapath......
  • 六年时间【万字总结】—kali Linux系统的各种报错问题解决方法,网络安全零基础入门到精
    是从玩kalilinux做主力物理机五六年时间中遇到问题,下面基本上是算是一个小总结吧,从Ubuntu到Kali到Arch在到Kali都是泪每一个问题都是要花费半天,甚至几个星期去解决的问题,下面问题总共时长估计有小半年花费时间研究问题解决和系统美化,折腾过linux应该都董不管是美化还是......
  • Ubuntu 22.04 编译安装 PHP 7.4.33 报错:make: *** [Makefile:749: ext/openssl/openss
     下载openssl1.1.1  https://openssl-library.org/source/old/1.1.1/index.html安装低版本OpenSSLwgethttps://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gztarzxvfopenssl-1.1.1w.tar.gzcdopenssl-1.1.1w./config--prefi......
  • 强化学习算法环境报错:python报错 —— gym DuplicatedInput doesn't exist
    运行强化学习pcl算法,报错,原因是gym版本过高,gym停止维护版本为0.26版本,降低版本到0.18,运行成功。pcl算法的官方地址:https://github.com/tensorflow/models/blob/master/research/pcl_rl......
  • 【报错解决】The Unicode character [进] at code point [36,827] cannot be encoded
    读取文件的时候报错:TheHTTPresponseheader[Content-Disposition]withvalue[attachment;filename="进度(导入模板).xlsx.xlsx"]hasbeenremovedfromtheresponsebecauseitisinvalidjava.lang.IllegalArgumentException:TheUnicodecharacter[进]atcodepoi......
  • 【报错解决】The Unicode character [进] at code point [36,827] cannot be encoded
    读取文件的时候报错:TheHTTPresponseheader[Content-Disposition]withvalue[attachment;filename="进度(导入模板).xlsx.xlsx"]hasbeenremovedfromtheresponsebecauseitisinvalidjava.lang.IllegalArgumentException:TheUnicodecharacter[进]atcodepoi......
  • 国标GB28181视频平台LiteGBS遇到摄像机报错用户被锁定该如何处理?
    在视频监控领域,LiteGBS国标GB28181软件作为一个广泛使用的软件,有时会遇到摄像机报错用户被锁定的问题。这种情况可能会阻碍监控系统的正常运行,影响安全监控的效率。为了解决这一问题,我们需要采取一系列标准化的检查和调整措施,以确保系统的稳定性和视频流的连续性。那么在应用中,......