本文目录导读:

我来详细解释如何使用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();
}
}
}
监控建议:
- 定期采集jstack:每30秒采集一次
- 设置告警阈值:阻塞线程数超过阈值时告警
- 自动恢复:检测到死锁时自动重启应用
通过以上方法,可以有效地检测和分析Java死锁及线程阻塞问题。