当前位置: 首页 > 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.gsyq.cn/news/1464355.html

相关文章:

  • Cartographer纯定位模式启动慢?手把手教你修改源码设置初始位姿,5分钟搞定快速重定位
  • SAP顾问转型记:手把手教你搞定Fiori Launchpad磁贴配置(以Manage Banks为例)
  • 告别漫长等待:Cartographer定位模式下自定义初始位姿的完整配置指南(附源码修改详解)
  • 华为健康数据TCX转换器:3步实现专业运动数据分析
  • 粉笔APP刷题对行测提分有帮助吗?资料分析、判断推理和言语这样练更有效
  • 2026年麻辣烫压面机免和面压面机/全自动压面机/压面机厂家综合对比分析 - 品牌宣传支持者
  • 智能筛选不再黑箱(可解释AI+决策溯源日志):从模型输出到人工复核的全链路审计方案
  • ESP32 GPIO实战:5分钟搞定按键检测与LED控制(附防抖动代码)
  • 别再手动算夹角了!用MATLAB调用STK的向量几何工具,5分钟搞定卫星姿态分析
  • 别再只盯着驻波比了!用VNA实测天线,这3个参数才是调优关键
  • 论文太单薄?资深导师力荐这几个AI论文工具
  • J-Flash设备列表配置详解:以添加华大半导体系列MCU为例,一篇搞定所有型号
  • 面向token编程,一夜百万账单,还能抗的住吗?
  • 别光看教程了!用Qt6+CMake亲手打造一个跨平台桌面小工具(附完整源码)
  • 新手福音:用快马AI生成你的第一个软件安装包,轻松掌握打包全流程
  • 实测对比:T94-2与T106-2磁环在无线充电LCC电感中的效率差异(附200股利兹线绕制心得)
  • Flutter项目上架AppStore,我踩过的permission_handler权限描述大坑(附完整Podfile配置)
  • 用MATLAB复现激光TEM模式光斑:从基模到高阶厄米特-高斯光束的完整仿真教程
  • 当markdown遇见快马AI:用自然语言描述生成带智能特性的复杂应用
  • ANSYS Fluent实现SLM/EBSM熔池仿真:小孔动态与锥形高斯热源参数配置指南
  • 2026年知名的食品彩箱/日用品彩箱/彩盒彩箱厂家综合对比分析 - 行业平台推荐
  • 613张真实室内盆栽图像数据集,含YOLOv5/v8兼容txt与PASCAL VOC标准xml标注
  • 初学者可用的LBM流动模拟代码包:含Poiseuille、Couette、液膜、圆柱绕流和Shan-Chen多相算例
  • 告别‘No FileSystem for scheme hdfs‘:HDP/CDH集群外客户端程序连接HDFS的完整配置流程
  • 多租户 RAG 权限绕过漏洞:元数据过滤被拼接注入,我们差点赔掉客户
  • 2026年知名的饮料彩盒彩箱/水果彩箱/化妆品彩盒彩箱/食品彩箱高口碑品牌推荐 - 品牌宣传支持者
  • 谷歌排名点击率重要吗?改了30个Title,老站流量直接翻倍
  • 2026年靠谱的临沂工商注册公司/临沂注册公司哪家强 - 品牌宣传支持者
  • 从手机充电到汽车BMS:聊聊那些被你忽略的‘低压部分’电路设计要点
  • 几何无衬线字体革命:Bebas Neue 开源项目的技术深度解析