主题切换
死锁相关
一、死锁产生场景
1. 定义
多个线程互相持有对方需要的锁,互相等待释放资源,所有线程永久阻塞,程序卡死无法继续执行。 核心诱因:线程需要同时获取多把锁,且加锁顺序不一致。
2. 标准死锁代码示例
java
Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(() -> {
synchronized (A) {
System.out.println("lock A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (B) {
System.out.println("lock B");
System.out.println("操作...");
}
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (B) {
System.out.println("lock B");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (A) {
System.out.println("lock A");
System.out.println("操作...");
}
}
}, "t2");
t1.start();
t2.start();- t1持有锁A,等待锁B;
- t2持有锁B,等待锁A;
- 双方互相等待,永久阻塞,形成死锁。
二、JDK命令行排查工具(线上首选)
1. jps
作用:列出当前所有JVM运行进程,获取Java进程PID。 执行命令:
shell
jps输出示例:
46032 Deadlock
30560 Jps46032 为死锁程序进程ID。
2. jstack
作用:打印指定进程所有线程堆栈,自动检测并标注死锁。 执行命令:
shell
jstack -l 46032日志关键标识:Found one Java-level deadlock:,会打印:
- 阻塞线程名称;
- 当前线程已持有的锁;
- 当前线程正在等待的锁;
- 持有目标锁的对方线程。
完整排查步骤
jps获取Java程序进程PID;jstack -l 进程号打印线程堆栈;- 检索日志中
deadlock关键字,定位死锁线程与对应代码行。
三、可视化GUI排查工具
1. jconsole
- 本质:基于JMX的GUI监控工具;
- 功能:监控JVM内存、类加载、线程状态,可检测死锁;
- 启动方式:JDK安装目录bin下执行
jconsole.exe。
2. VisualVM(jvisualvm)
- 功能:线程监控、内存快照、CPU占用分析、堆内存查看,自带死锁检测;
- 启动方式:JDK bin目录执行
jvisualvm.exe; - 优势:图形化展示线程持有/等待锁关系,定位更直观。
四、总结
- 死锁高发场景:多线程需要同时抢占多把锁,且加锁顺序混乱。
- 线上快速诊断方案:
jps查进程PID →jstack导出线程栈,检索deadlock定位死锁代码。 - 本地调试可视化工具:jconsole、VisualVM,图形化查看线程锁依赖关系。
五、面试高频问答
问:什么场景容易产生死锁?答: 多个线程需要同时获取多把锁,且线程之间加锁顺序不一致,互相持有对方依赖的锁,形成循环等待。
问:线上程序出现死锁,如何排查?答: 先用jps获取Java进程PID,再执行jstack打印线程堆栈,日志会自动输出死锁线程、持有锁、等待锁与对应代码位置;本地可使用jconsole、VisualVM可视化排查。
问:jps和jstack各自作用?答: jps列出所有运行的JVM进程,拿到进程ID;jstack打印进程内全部线程堆栈,自动识别并打印死锁信息。