使用jstack排查死锁,面试考点
使用 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...a0Thread-0 持有0x...a0等待0x...b0代码位置具体到哪个类的哪一行如DeadlockExample.java:255. 解决死锁根据堆栈定位到代码常见解决方式调整锁顺序确保所有线程以相同的顺序获取锁使用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-17. 注意事项如果进程占用 CPU 过高也可用jstack查看线程状态结合top -H -p pid找到高 CPU 线程 ID 转十六进制后匹配堆栈。多次生成快照间隔几秒有助于对比线程状态变化。在容器中可能需要进入容器内部执行jstack或使用docker exec。若jstack不可用如 JVM 未安装完整 JDK可使用kill -3 pid将线程快照输出到标准错误通常记录在应用日志中。通过以上步骤可以快速定位并解决 Java 应用中的死锁问题。