当前位置: 首页 > news >正文

死锁产生条件与诊断:jps、jstack、VisualVM

死锁题很容易被答成一句话:两个线程互相等待。

这句话当然对,但面试里不够。更完整的回答应该包括三层:

  1. 死锁怎么写出来的。
  2. 死锁成立需要哪些条件。
  3. 线上或本地怎么定位。

一个最典型的死锁

两个线程分别持有一把锁,又去等待对方手里的锁。

ObjectA=newObject();ObjectB=newObject();Threadt1=newThread(()->{synchronized(A){System.out.println("lock A");sleep(1000);synchronized(B){System.out.println("lock B");}}},"t1");Threadt2=newThread(()->{synchronized(B){System.out.println("lock B");sleep(500);synchronized(A){System.out.println("lock A");}}},"t2");t1.start();t2.start();

执行到某个时刻:

线程t1持有锁 A,等待锁 B。

线程t2持有锁 B,等待锁 A。

谁都不肯放,谁都往下走不了。

线程 t2锁 B锁 A线程 t1线程 t2锁 B锁 A线程 t1互相等待,程序无法继续获取锁 A获取锁 B等待锁 B等待锁 A

死锁的四个必要条件

经典死锁有四个必要条件:

条件含义
互斥条件资源同一时刻只能被一个线程占有
占有且等待线程持有资源的同时,还在等待其他资源
不可抢占资源不能被外部强行剥夺,只能由持有者释放
循环等待多个线程形成首尾相接的等待环

死锁成立

互斥

占有且等待

不可抢占

循环等待

锁一次只能一个线程持有

拿着 A 等 B

别人不能强行夺走 A

t1 等 t2, t2 等 t1

预防死锁,本质就是破坏其中一个条件。工程上最常见的是破坏循环等待:固定加锁顺序。

比如所有线程都先拿 A,再拿 B,就不会出现一个拿 A 等 B、另一个拿 B 等 A。

线程 t1

先获取锁 A

线程 t2

再获取锁 B

执行临界区

怎么用 jps 和 jstack 诊断

当 Java 程序疑似卡住,可以先用jps找进程。

jps-l

找到目标进程 ID 后,用jstack打线程栈:

jstack-l<pid>

如果确实发生死锁,jstack通常会在输出里给出类似信息:

Found one Java-level deadlock:

并列出哪些线程持有哪些锁、正在等待哪些锁。

诊断链路可以这样看:

程序卡死 / 请求无响应

jps -l 找 Java 进程

jstack -l pid 导出线程栈

是否发现 deadlock

查看线程名、锁对象、代码行

回到代码分析加锁顺序

继续排查 BLOCKED / WAITING / IO 等问题

jstack的价值不只是告诉你“有死锁”。更关键的是告诉你线程卡在哪一行、等哪把锁、这把锁被谁持有。

可视化工具

本地开发或测试环境,也可以用 GUI 工具看线程。

工具作用
jconsole监控 JVM 内存、线程、类加载等信息
VisualVM查看线程、CPU、内存、堆栈、对象分配等

它们适合辅助定位,但线上排障不要只依赖 GUI。最通用、最容易落地的还是jps+jstack,因为服务器上通常能直接执行。

如何修复死锁

修复死锁不要只盯着“加大超时时间”。超时只能缓解,不能解释为什么锁顺序会错。

常见修复方式:

  1. 固定加锁顺序,所有线程都按同一顺序获取多把锁。
  2. 缩小锁粒度,减少一次持有多把锁的场景。
  3. tryLock加超时,获取不到锁时主动释放已持有资源并重试。
  4. 避免在持锁期间调用外部服务、数据库、RPC 这类不可控耗时操作。

tryLock的思想大概是:

if(lockA.tryLock(1,TimeUnit.SECONDS)){try{if(lockB.tryLock(1,TimeUnit.SECONDS)){try{// do something}finally{lockB.unlock();}}}finally{lockA.unlock();}}

这不是鼓励所有地方都写复杂的tryLock,而是说明:当业务确实需要多把锁时,至少要设计失败退出路径。

面试怎么答

可以这样答:

死锁通常发生在多个线程需要同时获取多把锁时。比如线程 1 持有 A 锁等待 B 锁,线程 2 持有 B 锁等待 A 锁,就会互相等待,程序无法继续。

死锁有四个必要条件:互斥、占有且等待、不可抢占、循环等待。解决死锁就是破坏其中一个条件,项目里最常见的是固定加锁顺序,避免循环等待。

诊断时可以先用jps -l找 Java 进程,再用jstack -l pid查看线程栈。如果有 Java 级死锁,jstack会打印 deadlock 信息,并指出线程持有和等待的锁。也可以用jconsole、VisualVM 这类工具辅助查看线程状态。

http://www.cnnetsun.cn/news/2759982.html

相关文章:

  • 从硬盘占用到授权费用:手把手教你避开ESXi 7.0、PVE和unRaid的隐藏成本坑
  • FPGA新手避坑指南:Quartus Prime 20.1精简版安装后,必做的3项验证(附Device Installer配置图解)
  • OpenClaw开源灵巧手:教学定位、能力边界与实操避坑指南
  • 保姆级教程:在Windows 10上从零安装Quartus II 13.1到点亮第一个LED(附USB-Blaster驱动避坑指南)
  • 初学者可用的LBM流动模拟代码包:含Poiseuille、Couette、液膜、圆柱绕流和Shan-Chen多相算例
  • Kinaxis推出前置部署工程服务,助力企业将决策转化为实际成果
  • 退休告别职场空虚度日,经营焦本味快餐,充实晚年增收实现老有所为
  • 全球仅17家持牌机构掌握的“动态合规路由”技术:AI驱动的智能汇款路径决策引擎揭秘
  • 如何使用隔空投送将文件从 iPhone传输到Mac?
  • 学生课堂扫码/手动签到App(含教师后台管理+本地SQLite数据存储)
  • 实验室的认证要求
  • FreeRTOS内存管理选型指南:为什么heap_4.c是嵌入式项目的首选(附heap_1到heap_5对比)
  • HP M126nw打印机实测:PS切片打印超长PDF的完整避坑指南(含Acrobat页眉页脚设置)
  • VMware克隆三台CentOS 7虚拟机后,别忘了检查这3个网络配置!否则集群搭建第一步就失败
  • AI Agent 产品冷启动:从技术 Demo 到杀手级价值产品的跨越
  • 跟着 MDN 学CSS day_50:(传统布局方法与网格系统)
  • 深入AXI GPIO中断机制:从Vivado勾选到SDK代码,如何捕获PL端按键的‘瞬间’?
  • 告别纯PS编程:在Zynq-7000上玩转AXI GPIO,让FPGA逻辑直接触发ARM中断
  • Xournal++:重新定义你的数字笔记体验,跨平台手写与PDF批注的终极解决方案
  • AWVS扫描DVWA实战:从78个漏洞报告看如何优化扫描策略与结果分析
  • 大数据小白也能入局!收藏这份大模型转型指南,高薪岗位等你来拿!
  • 告别VBA!用Visual Studio 2019给Excel做个Ribbon插件(VSTO入门实战)
  • 知识库问答翻车了?我的Agent方案比传统FAQ搜索强在哪
  • Matlab单变量时序预测工具:SSA自动调优LSTM,含数据预处理、误差评估与可视化
  • AI 自动生成 Mock 数据:微服务接口的 Schema 解析与 Prompt 注入机制
  • HMS Core 5.2.0实战:用Network Kit给你的App网络请求和文件下载‘换芯’提速
  • 零信任安全架构与动态权限管理系统技术方案
  • 彻底搞懂IDEA文件编码:为什么设置了UTF-8还会报‘wrong encoding’?
  • 某金融 Agent 一天烧掉 2 万 API 费用,只因工具调用写了死循环
  • 2026张家界市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐