Java死锁jstack检测阻塞

wen java案例 2

本文目录导读:

Java死锁jstack检测阻塞

  1. 死锁示例代码
  2. 获取jstack信息
  3. jstack输出分析
  4. 阻塞线程检测
  5. 常见线程状态及含义
  6. 自动化检测脚本
  7. 实用工具推荐
  8. 预防和解决建议

我来详细解释如何使用jstack检测Java死锁和线程阻塞问题。

死锁示例代码

public class DeadlockDemo {
    private static final Object resource1 = new Object();
    private static final Object resource2 = new Object();
    public static void main(String[] args) {
        // 线程1:先锁resource1,再锁resource2
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1: locked resource1");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {}
                synchronized (resource2) {
                    System.out.println("Thread 1: locked resource2");
                }
            }
        });
        // 线程2:先锁resource2,再锁resource1
        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2: locked resource2");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {}
                synchronized (resource1) {
                    System.out.println("Thread 2: locked resource1");
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

获取jstack信息

方法1:使用jstack命令

# 先找到Java进程ID
jps -l
# 输出线程堆栈
jstack -l <PID>
# 或输出到文件分析
jstack -l <PID> > thread_dump.txt

方法2:使用jcmd命令

jcmd <PID> Thread.print

方法3:使用kill -3(Linux)

kill -3 <PID>
# 堆栈信息会输出到标准输出

jstack输出分析

死锁时jstack会明确输出死锁信息:

死锁检测输出示例:

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007f8b7c006800 (object 0x000000076b5f6a70, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007f8b7c006a00 (object 0x000000076b5f6a80, a java.lang.Object),
  which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
    at DeadlockDemo.lambda$main$1(DeadlockDemo.java:25)
    - waiting to lock <0x000000076b5f6a70> (a java.lang.Object)
    - locked <0x000000076b5f6a80> (a java.lang.Object)
"Thread-0":
    at DeadlockDemo.lambda$main$0(DeadlockDemo.java:14)
    - waiting to lock <0x000000076b5f6a80> (a java.lang.Object)
    - locked <0x000000076b5f6a70> (a java.lang.Object)

线程状态分析:

"Thread-0" #12 prio=5 os_prio=0 tid=0x00007f8b7c006800 nid=0x1234 waiting for monitor entry
   java.lang.Thread.State: BLOCKED (on object monitor)

阻塞线程检测

阻塞状态的几种类型:

public class BlockingDemo {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        // 线程1:持有锁并长时间等待
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread 1: holding lock");
                try {
                    Thread.sleep(10000); // 模拟长时间持有锁
                } catch (InterruptedException e) {}
            }
        });
        // 线程2:等待获取锁(BLOCKED状态)
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread 2: acquired lock");
            }
        });
        thread1.start();
        thread2.start();
    }
}

jstack显示的阻塞状态:

"Thread-2" #14 prio=5 os_prio=0 tid=0x00007f8b7c007000 nid=0x1235 waiting for monitor entry
   java.lang.Thread.State: BLOCKED (on object monitor)
    at BlockingDemo.lambda$main$1(BlockingDemo.java:13)
    - waiting to lock <0x000000076b5f6a90> (a java.lang.Object)
    - locked <0x000000076b5f6a90> (a java.lang.Object)

常见线程状态及含义

状态 含义 jstack显示
RUNNABLE 正在运行 java.lang.Thread.State: RUNNABLE
BLOCKED 等待锁 java.lang.Thread.State: BLOCKED (on object monitor)
WAITING 等待通知 java.lang.Thread.State: WAITING (on object monitor)
TIMED_WAITING 超时等待 java.lang.Thread.State: TIMED_WAITING (sleeping)

自动化检测脚本

#!/bin/bash
# detect_deadlock.sh
# 查找Java进程
JAVA_PID=$(jps -l | grep DeadlockDemo | awk '{print $1}')
if [ -z "$JAVA_PID" ]; then
    echo "Java process not found"
    exit 1
fi
# 获取jstack并检测死锁
jstack -l $JAVA_PID > /tmp/jstack_${JAVA_PID}.txt
# 检测死锁
if grep -q "Found one Java-level deadlock" /tmp/jstack_${JAVA_PID}.txt; then
    echo "=== DEADLOCK DETECTED ==="
    # 提取死锁详细信息
    awk '/Found one Java-level deadlock/,/^$/' /tmp/jstack_${JAVA_PID}.txt
    # 发送告警
    echo "Send alert: Deadlock detected in process $JAVA_PID"
fi
# 统计阻塞线程数
BLOCKED_COUNT=$(grep -c "BLOCKED" /tmp/jstack_${JAVA_PID}.txt)
echo "Blocked threads count: $BLOCKED_COUNT"
# 输出所有阻塞线程
grep -B 5 "BLOCKED" /tmp/jstack_${JAVA_PID}.txt

实用工具推荐

使用VisualVM(可视化工具)

# 启动VisualVM
jvisualvm

使用fastthread.io(在线分析)

  • 上传jstack文件到 https://fastthread.io/
  • 自动分析死锁和线程问题

使用Thread Dump Analyzer

# 下载并运行
java -jar tda.jar

预防和解决建议

代码层面预防:

public class SafeLockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    public void safeMethod() {
        // 保持一致的锁顺序
        synchronized (lock1) {
            synchronized (lock2) {
                // 业务逻辑
            }
        }
    }
    // 使用tryLock避免死锁
    public void tryLockMethod() {
        Lock lock = new ReentrantLock();
        try {
            if (lock.tryLock(1, TimeUnit.SECONDS)) {
                try {
                    // 业务逻辑
                } finally {
                    lock.unlock();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

监控建议:

  1. 定期采集jstack:每30秒采集一次
  2. 设置告警阈值:阻塞线程数超过阈值时告警
  3. 自动恢复:检测到死锁时自动重启应用

通过以上方法,可以有效地检测和分析Java死锁及线程阻塞问题。

抱歉,评论功能暂时关闭!