工作十几年,第一次在线上遇到死锁问题
创始人
2025-05-31 17:25:19

概述


最近一直在为系统的稳定性努力着,但凡线上有一些问题,都不轻易放过。尤其是在2023年,大环境不好的情况下,如果it团队系统稳定性都做的不好的话,很容易提桶走人的。

事情是这样的,在2023年3月8日的晚上七点左右,调用B服务RPC接口的其他服务,都陆续开始报【接口调用超时异常】,B服务已经有一个多月没有上线过了,而出故障的时间当天,流量也没陡增。

这种突然出问题,但跟流量和发版又没有关系的,一般就是先重启,因为大概率是触发某个隐藏的bug导致服务慢慢不可用了。注意,当时是没让运维dump pod的运行信息的,因为线上报错的信息比较多了,也影响到了用户,只能先止损。果然重启后,错误信息立马消失了,一直到当天凌晨,都没有再报错了。

但是单单看一堆超时的错误信息,一时之间,是很难找出根因的。那天我一直看到了晚上11点,只是得到一个粗浅的结论:

错误信息,集中在B服务的某些pod上,有蛮多线程block住了。

隔天早上回到办公室后,就申请让B服务开通arms(阿里的应用实时监控服务),坐等错误再次发生。阿里的arms还是很强大的,但是是付费的且不便宜,一般平时不开的。

一直等到了3月9号的下午五点多,B服务的接口又开始超时了,这次我赶紧到arms的事件中心大盘里,看看有无异常的事件发生,猜我看到了啥?

image.png

居然有死锁,生平第一次在线上遇到过。查看了arms打印出来的详细日志,发现是两个线程,在两个ConcurrentHashMap对象之间,相互等待了。

[ARMS] Found deadlock:
"thread_14" Id=xxxx BLOCKED on java.util.concurrent.ConcurrentHashMap$Node@687bfd0d owned by "Dubbo-thread-499" Id=1044
at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1027)
- blocked on java.util.concurrent.ConcurrentHashMap$Node@687bfd0d
at java.util.concurrent.ConcurrentHashMap.putIfAbsent(ConcurrentHashMap.java:1535)"Dubbo-thread-499" Id=cccc BLOCKED on java.util.concurrent.ConcurrentHashMap$ReservationNode@2205946f owned by "thread_14" Id=yyy
at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1027)
- blocked on java.util.concurrent.ConcurrentHashMap$ReservationNode@2205946f
at java.util.concurrent.ConcurrentHashMap.putIfAbsent(ConcurrentHashMap.java:1535)

也就是说:

  • 线程thread_14在已获得某种资源后,还想继续获取687bfd0d对象的锁,而这把锁整被线程Dubbo-thread-499拿在手上;
  • 线程Dubbo-thread-499在已获得某种资源后,还想继续获取2205946f对象的锁,而这把锁整被线程thread_14拿在手上;

但是出自Doug Lea大神的ConcurrentHashMap怎么可能出现死锁呢? 于是就在本地简单写了一段程序验证了一下:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class TestConcurrentMapDeadlock {public static void main(String[] args) {Map concurrentHashMap1 = new ConcurrentHashMap<>(16);Map concurrentHashMap2 = new ConcurrentHashMap<>(16);new Thread(() -> concurrentHashMap1.computeIfAbsent("a", key -> {concurrentHashMap2.computeIfAbsent("b", key2 -> 2);return 1;})).start();new Thread(() -> concurrentHashMap2.computeIfAbsent("b", key -> {concurrentHashMap1.computeIfAbsent("a", key2 -> 2);return 1;})).start();}
}

在Intellij idea上运行上面的代码,并使用idea自带的Dump Threads功能,会发现真的触发死锁了。

image.png

image.png

后来看了一下jdk 1.8的ConcurrentHashMap的computeIfAbsent源代码,在并发的情况下,确实有概率性会触发死锁。

image.png

大概的执行序列是:

  • 1、生成ReservationNode预占节点;
  • 2、对该节点进行加锁(这里是重点),然后将该节点放入指定key的槽位中;
  • 3、执行我们传入的计算逻辑,当我们计算逻辑中包含有computeIfAbsent时,此时代码会重复上面的1~3步骤

到这里就大概明白了,当执行一次computeIfAbsent的嵌套逻辑时,会有两个ReservationNode对象会被加锁,那在并发的情况下,是可能会产生死锁的。

那具体是哪行代码触发的呢? 其实阿里的arms是有完整打印出来的,由于有敏感信息,这里不能贴出来。但是触发的诱因可以说一下:

线程thread_14,是想更新一个用户的手机号信息,对应的代码逻辑会操作两个ConcurrentHashMap,先操作map1,再操作map2,这个两个map是作为本地缓存使用的,都会对其进行computeIfAbsent操作。而Dubbo-thread-499也是一样,也会操作这两个map,先操作map2,再操作map1。当有并发的情况下,处理的又是同一个手机号的时候,就可能触发死锁。

至于thread_14操作完map1这个本地缓存后,为啥还要去操作map2这个本地缓存? 我看了业务逻辑实现后,发现是没有必要的,因为这两份本地缓存的数据,都有对应的业务逻辑代码去保证它的准确性。后来问了一下开发这块的老同事,得到的回复是:

顺便更新一下另外一个map,提升一些性能。

好吧,这个就真的是好心做坏事了。

解决这次的死锁的方案也很简单,就是断掉其中一条路,避免死锁就可以了。正如刚才上面分析的,两份本地缓存都有各自的业务逻辑去确保它的准确性,没必要顺手去更新别人家的缓存

在2023年3月16日发版后,直到今天,2023年3月20日,暂时没有死锁的告警了。

上一篇:每日学术速递3.21

下一篇:split()详解

相关内容

热门资讯

4月广州消费品市场表现强劲 1-4月,随着消费品以旧换新等促消费政策持续发力和各类会展活动陆续开展,政策相关消费快速增长,升级类...
金价,又跌了! 人民财讯5月31日电,5月30日,COMEX黄金期货收跌0.92%,报3313.1美元/盎司。 从高...
10万吨改性项目!巴斯夫、金发... 【DT新材料】获悉,6月3日,沪市主板新股海阳科技将启动申购,上市在即! 资料显示,海阳科技前身为南...
湾财周报|大事记 比亚迪驳斥“... 一周大事记(5月26日-6月1日) 头条 比亚迪驳斥! 长城“车圈恒大论”是行业警示还是危言耸听?...
通源石油跌1.96%,成交额1... 5月30日,通源石油跌1.96%,成交额1.03亿元,换手率4.40%,总市值23.54亿元。 异动...
中国邮储银行浙江分行2025校... 点这里 ↑ 老满说高考 作者 l 老满 生涯规划师l 升学顾问l 拆书家 这是 老满说高考公众号 的...
公募基金规模首次突破33万亿元... 每经记者:肖芮冬 每经编辑:叶峰 天赐良基日报第654期 一、今日基金新闻速览 1、华润元大基金贾...
湾财周报 大事记 比亚迪驳斥“... 一周大事记(5月26日-6月1日)头条比亚迪驳斥!长城“车圈恒大论”是行业警示还是危言耸听?近日,关...
EL表达式JSTL标签库 EL表达式     EL:Expression Language 表达式语言     ...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
工信部、中汽协紧急发声!汽车“... 文/刘育英新一轮汽车价格战再起。近日,工信部、中汽协纷纷发声表示反对。工业和信息化部表示,将加大对汽...
3 ROS1通讯编程提高(1) 3 ROS1通讯编程提高3.1 使用VS Code编译ROS13.1.1 VS Code的安装和配置...
募资39亿,全亏光了,账上不到... 关于天然气,用户的感觉是价格一直在上涨,但很奇怪,不管怎么涨,天然气企业仍然亏,还亏得一塌糊涂。这是...
资阳房产评估公司 这是(tel-15828298733)整理的信息,希望能帮助到大家 在当今社会,随着经济的发展和城...
华桥汇利(中国)投资基金管理有... 今年第一季度,美国企业利润出现大幅下降,且面临着来自关税上升的持续压力,这一局面可能会在今年进一步加...
ESG 报告合规与鉴证:全球政... 在当下全球经济格局里,ESG(环境、社会和公司治理)已然成为衡量企业可持续发展能力的关键指标。随着全...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...
与锤巨子生物的大嘴博士持股同一... 医美龙头巨子生物“成分争议”风波持续发酵。日前,美妆博主大嘴博士(香港大学化学博士郝宇)发文,质疑巨...
Linux之进程间通信 目录 进程间通信介绍 一、为什么要进行进程间通信? 二、进程间通信目的 三、进程间通信...
从“造城”到“留客”,文旅局长... 你有没有刷到最近各地文旅局局长全体“尬舞”的视频?领导们放下架子开始跳魔性舞蹈,这场舞的背后啊,可不...
Hazel引擎学习(十一) 我自己维护引擎的github地址在这里,里面加了不少注释,有需要的可以看...
孩子的教育金,分享3个「有效」... 点击 “简七读财” ,发送消息“ 理财小工具 ”免费领取“40个赚钱工具资源包”晚上好,我是简七编...
iZotope RX 10(专... iZotope RX 10是一款专业的音频修复和增强软件,具有音频修复工具、音频增强工...
我的docker随笔40:cl... 本文介绍 clickhouse 数据库的容器化部署。 起因 某项目需生产环境数据库,因...
透视一周牛熊股:最牛股路桥信息... 过去一周(5月26日—5月30日)A股三大指数集体下跌。截至5月30日收盘,上证指数报3347.49...
基于matlab创建地面固定雷... 一、前言此示例演示如何创建和显示包含地面固定雷达、转弯飞机、等速飞机和移动地面车辆的多平台方案。二、...
暗夜发光,独自闪耀,盘点网页暗... 众所周知,网页的暗黑模式可以减少屏幕反射和蓝光辐射,减少眼睛的疲劳感&#...
C语言-程序环境和预处理(2) 文章目录预处理详解1.预定义符号2.#define2.1#define定义的标识符2.2#defin...
MySQL数据库知识整理 MySQL数据库知识整理 MySQL事务详解 事务四大特性ACID 原子性(Atomi...
Docker基础篇——最全讲解 文章目录一、CentOS安装docker二、启动帮助类命令三、镜像命令1.名词概念2.常用命令2.1...