当前位置: 首页 > 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.jsqmd.com/news/952740/

相关文章:

  • MATLAB图像处理:用IFFT2验证你的FFT2算法到底对不对(附完整代码)
  • 【AI养老革命白皮书】:2024年全球7大智能退休工具实测对比与适配指南(含养老金收益率提升37%的隐藏配置)
  • Cartographer纯定位模式启动慢?手把手教你修改源码设置初始位姿,5分钟搞定快速重定位
  • 微信PC版小程序包.wxapkg解密工具(Node.js命令行版,支持Win/macOS)
  • 告别手动标注!用NakiPipeline插件为PDMS管道设计自动化提速(保姆级配置指南)
  • SAP顾问转型记:手把手教你搞定Fiori Launchpad磁贴配置(以Manage Banks为例)
  • 保姆级教程:在Windows 10上从零安装Quartus II 13.1并完成第一个FPGA工程(附USB-Blaster驱动配置)
  • 从官方视频到落地项目:手把手带你复现PaddleOCR数字识别实战(AI Studio保姆级教程)
  • CZSC缠论分析插件:通达信智能量化交易终极指南
  • 让AI成为设计伙伴:使用快马平台智能优化数字后端时序收敛难题
  • Anaconda安装后必做的5件事:从验证安装到用conda高效管理Python包(Python 3.8版)
  • 双击即玩的Python彩色飞机大战:带图文教程、源码和独立exe
  • 华为健康数据TCX转换器:3步实现专业运动数据分析
  • 告别漫长等待:Cartographer定位模式下自定义初始位姿的完整配置指南(附源码修改详解)
  • 别再找在线工具了!用Photoshop手动制作QQ/微信隐藏图(附PNG保存避坑指南)
  • 粉笔APP刷题对行测提分有帮助吗?资料分析、判断推理和言语这样练更有效
  • ABB变频器备件IGBT模块FS300R12KE3/AGDR-72CS
  • 2026年麻辣烫压面机免和面压面机/全自动压面机/压面机厂家综合对比分析 - 品牌宣传支持者
  • 从磁带机到SSD:聊聊那些你可能听过但没见过的存储器(磁芯、磁表面、光存储)
  • 手把手教你用Vivado仿真Xilinx SelectIO IP核(附Testbench源码解析)
  • 从仿真时间设置到结果解读:FDTD谐振腔Q值计算的全流程避坑指南
  • 硝酸体系核关联假说解析
  • 别只盯着S参数了!HFSS中电压源、电流源激励的另类用法与场分析实战
  • 告别编译踩坑:用我写的批处理脚本,5分钟在Windows上搞定Paho MQTT C/C++库(支持VS2017/2019)
  • Bobst 704-1257-02电机控制板
  • 从仿真到实物:如何将Matlab Robotic Toolbox里的四轴机械臂模型‘搬’到Arduino上控制?
  • 实战前端设计:基于快马AI生成一个可拖拽的任务管理看板应用
  • ESP32 GPIO实战:5分钟搞定按键检测与LED控制(附防抖动代码)
  • 智能筛选不再黑箱(可解释AI+决策溯源日志):从模型输出到人工复核的全链路审计方案
  • GLM-5.1登顶SWE-Bench Pro:中文代码智能体的工程化突破