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

原文:记一次 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: 52at 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 条件满足,去释放锁,结果可想而知,当前线程释放别的线程的锁,依旧会抛异常,而这个异常正是文章开头说到的那个异常。同样不推荐该方式。

判断持有者

这种写法更优雅:

判断持有者

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

持有者释放

如下图所示:

开心一刻

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/862219.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

您的公司需要小型语言模型

当专用模型超越通用模型时 “越大越好”——这个原则在人工智能领域根深蒂固。每个月都有更大的模型诞生,参数越来越多。各家公司甚至为此建设价值100亿美元的AI数据中心。但这是唯一的方向吗? 在NeurIPS 2024大会上,OpenAI联合创始人伊利亚苏茨克弗提出了一个观点:“我们所…

2024年12月总结及随笔之1T资料灭失

2024年12月总结及随笔之1T资料灭失1. 回头看 日更坚持了731天。读《数据质量管理:数据可靠性与数据质量问题解决之道》更新完成 读《图数据库实战》更新完成 读《数据保护:工作负载的可恢复性》开更并持续更新2023年至2024年12月底累计码字1834939字,累计日均码字2510字。 2…

JDK 23 新特性解析

1. 前言 JDK 23 是 Java SE 平台版本 23 的参考实现,2024 年 9 月 17 日正式发布。本文将详细解析 JDK 23 的主要新特性。 2. 新特性总览JEP 特性名称 状态 说明455 模式中的原始类型、instanceof 和 switch 预览 扩展模式匹配,允许在所有模式上下文中使用原始类型,并在 ins…

关于 Pycharm 2024 安装激活使用教程以及常见问题(激活至2026,实际上永久,亲测!)

申明:本教程 Pycharm补丁、激活码均收集于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除。若条件允许,希望大家购买正版 !卸载老版本 Pycharm 首先,如果小伙伴的电脑上有安装老版本的 Pycharm , 需要将其彻底卸载掉,如下所示(没有安装则不用管,直接安装即…

待完成

简介 协程 执行前、执行中、执行后 全部都可以被完美正确的取消;执行前取消 这个好理解; 执行中,是协程内核 尝试取消;若开发者内部是大耗时协程,开发者自己也可以 通过 IsCancel 判断来结束协程; 执行后,但是有可能衍生出来了很多子协程,这些子协程又是有 前、中、后 …

Hello World from RVMaker!

RVMaker 的全称是 RISC-V Maker,一个专注于 RISC-V MCU 生态的平台。欢迎关注 RVMaker(RISC-V Maker),一个专注于 RISC-V MCU 生态的平台。 这里分享最新的 RISC-V MCU 开发教程、技术文章与应用方案,帮助开发者深入了解并高效应用 RISC-V MCU。 RVMaker 致力于推动 RISC-…

挚科龙芯机器信息

本文记录一台挚科(ZHIKE)龙芯的机器信息记录时间: 2024.12.31 机器型号:ZKL360-TF 龙芯旧世界 3A6000 集成显卡 系统信息如下 root@zhike-pc:~# cat /etc/os-release PRETTY_NAME="Loongnix GNU/Linux 20 (DaoXiangHu)" NAME="Loongnix GNU/Linux" VER…

使用已知的p、q生成私钥解rsa密文的方法

昨天渗透赛的一道题,研究了一下颇有感触,给大家分享一下(2024年的最后一天还要坐牢呜呜呜)先用rsatool根据已知的p、q生成公钥 python rsatool.py -f DER -o key.der -p 31764044218067306492147889531461768510318119973238219147743625781223517377940974553025619071173…

方差分析1.1

2 方差分析 2.1 单因素方差分析 2.1.1 分析基础 若控制变量有k个水平,不同水平下各观测变量的总体均值记为μ1,μ2,…,μk,则单因素方差分析的原假设为μ1 = μ2 = … = μk,即各总体均值都相等。 单因素方差分析认为,观测变量值的变动受到控制变量和随机变量两方面的影…

IOS FrameWorks探索

IOS里的库 https://www.jianshu.com/p/b0f58bae27db https://juejin.cn/post/6950926098595053582 静态库形式: .a和.framework 1.静态库在编译时加载,链接时会完整的复制到可执行文件中。 2.静态库的可执行文件通常会比较大,因为所需的数据都会被整合到目标代码中,因此编译…

Khronos计算加速

Khronos现行标准Khronos标准与计算、嵌入式、视觉和安全关键型市场最相关。Khronos现行标准,如图1-3所示。图1-3 Khronos现行标准Khronos计算加速标准Khronos计算加速标准,如图1-4所示。图1-4 Khronos计算加速标准机器学习加速APIKhronos机器学习加速API,如图1-5所示。 图1-…

推荐两本书《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》

两本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该书强力解析AI芯片的核心技术开发,内容翔实、知识点新…