Java使用jstack排查死锁(面试考点)
作者:砍材农夫
jstack 是 JDK 自带的命令行工具,用于生成 Java 进程的线程快照(thread dump)。通过分析线程快照,可以快速定位死锁问题。
1. 获取 Java 进程 ID
首先,找到目标 Java 进程的 PID(进程 ID)。使用 jps 命令:
jps -l
输出示例:
12345 com.example.MyApplication
67890 sun.tools.jps.Jps
记录下应用进程的 PID,例如 12345。
2. 生成线程快照
使用 jstack 命令生成线程快照:
jstack <pid> > thread_dump.txt
例如:
jstack 12345 > thread_dump.txt
如果应用因死锁导致无响应,可以添加 -l 选项(显示锁信息):
jstack -l 12345 > thread_dump.txt
注意:在某些环境下(如容器),可能需要使用 sudo 或以应用运行用户身份执行。
3. 分析线程快照
打开生成的 thread_dump.txt,查找死锁信息。
3.1 查找死锁摘要
jstack 会在线程快照末尾自动检测并输出死锁摘要,类似:
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f8b5c00a5a8 (object 0x00000000d5f6e3a0, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f8b5c00b0b0 (object 0x00000000d5f6e3b0, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.example.DeadlockExample.method2(DeadlockExample.java:25)
- waiting to lock <0x00000000d5f6e3a0> (a java.lang.Object)
- locked <0x00000000d5f6e3b0> (a java.lang.Object)
at com.example.DeadlockExample.run(DeadlockExample.java:15)
"Thread-0":
at com.example.DeadlockExample.method1(DeadlockExample.java:18)
- waiting to lock <0x00000000d5f6e3b0> (a java.lang.Object)
- locked <0x00000000d5f6e3a0> (a java.lang.Object)
at com.example.DeadlockExample.run(DeadlockExample.java:10)3.2 手动分析线程堆栈
如果没有自动摘要(例如使用 jstack 的旧版本),可以手动查找以下特征:
- BLOCKED 状态:线程状态为 BLOCKED(在 java.lang.Thread.State 中)
- 等待锁:堆栈中包含 waiting to lock <0x...> 和 locked <0x...>
找出相互等待锁的线程对,即线程 A 持有锁 L1 等待锁 L2,线程 B 持有锁 L2 等待锁 L1。
4. 解读死锁信息
从摘要中可以获取:
- 死锁线程名称:Thread-1 和 Thread-0
- 锁对象地址:<0x00000000d5f6e3a0> 和 <0x00000000d5f6e3b0>
- 持有与等待关系:
- Thread-1 持有 <0x...b0>,等待 <0x...a0>
- Thread-0 持有 <0x...a0>,等待 <0x...b0>
- 代码位置:具体到哪个类的哪一行(如 DeadlockExample.java:25)
5. 解决死锁
根据堆栈定位到代码,常见解决方式:
- 调整锁顺序:确保所有线程以相同的顺序获取锁
- 使用 tryLock:尝试获取锁,超时则释放已持有的锁
- 减少锁粒度:缩小同步块范围
- 使用更高层次的并发工具:如 ReentrantLock、StampedLock、ConcurrentHashMap 等
6. 完整示例
以下是一个简单的死锁代码及使用 jstack 排查的演示。
6.1 死锁代码
public class DeadlockExample implements Runnable {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
@Override
public void run() {
if (Thread.currentThread().getName().equals("Thread-0")) {
method1();
} else {
method2();
}
}
private void method1() {
synchronized (lock1) {
System.out.println("Thread-0 持有 lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) {
System.out.println("Thread-0 持有 lock2");
}
}
}
private void method2() {
synchronized (lock2) {
System.out.println("Thread-1 持有 lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) {
System.out.println("Thread-1 持有 lock1");
}
}
}
public static void main(String[] args) {
DeadlockExample example = new DeadlockExample();
Thread t1 = new Thread(example, "Thread-0");
Thread t2 = new Thread(example, "Thread-1");
t1.start();
t2.start();
}
}6.2 使用 jstack 排查
# 1. 找到 PID jps -l # 2. 生成快照 jstack -l 12345 > deadlock.txt # 3. 查看死锁摘要 grep -A 20 "Found one Java-level deadlock" deadlock.txt
输出类似:
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f8b5c00a5a8 (object 0x00000000d5f6e3a0, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f8b5c00b0b0 (object 0x00000000d5f6e3b0, a java.lang.Object),
which is held by "Thread-1"
7. 注意事项
- 如果进程占用 CPU 过高,也可用 jstack 查看线程状态,结合 top -H -p <pid> 找到高 CPU 线程 ID 转十六进制后匹配堆栈。
- 多次生成快照(间隔几秒)有助于对比线程状态变化。
- 在容器中,可能需要进入容器内部执行 jstack,或使用 docker exec。
- 若 jstack 不可用(如 JVM 未安装完整 JDK),可使用 kill -3 <pid> 将线程快照输出到标准错误(通常记录在应用日志中)。
通过以上步骤,可以快速定位并解决 Java 应用中的死锁问题。
到此这篇关于Java使用jstack排查死锁(面试考点)的文章就介绍到这了,更多相关Java jstack排查死锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
