本文目录导读:

我为您提供一个完整的Java死锁案例以及详细的排查方案。
死锁案例代码
import java.util.concurrent.TimeUnit;
public class DeadLockDemo {
// 定义两个资源锁
private static final Object lockA = new Object();
private static final Object lockB = new Object();
public static void main(String[] args) {
// 线程1:先获取lockA,再获取lockB
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + " 获取了lockA");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 尝试获取lockB");
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + " 获取了lockB");
}
}
}
}, "Thread-1");
// 线程2:先获取lockB,再获取lockA
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + " 获取了lockB");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 尝试获取lockA");
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + " 获取了lockA");
}
}
}
}, "Thread-2");
thread1.start();
thread2.start();
}
}
更复杂的死锁案例(包含多个锁和嵌套调用)
import java.util.List;
import java.util.ArrayList;
public class ComplexDeadLock {
private final List<String> list1 = new ArrayList<>();
private final List<String> list2 = new ArrayList<>();
public void addToList1(String value) {
synchronized (list1) {
System.out.println(Thread.currentThread().getName() + " 锁定list1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
list1.add(value);
// 嵌套获取list2
synchronized (list2) {
list2.add(value);
System.out.println(Thread.currentThread().getName() + " 同时锁定list1和list2");
}
}
}
public void addToList2(String value) {
synchronized (list2) {
System.out.println(Thread.currentThread().getName() + " 锁定list2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
list2.add(value);
// 嵌套获取list1
synchronized (list1) {
list1.add(value);
System.out.println(Thread.currentThread().getName() + " 同时锁定list2和list1");
}
}
}
public static void main(String[] args) {
ComplexDeadLock deadLock = new ComplexDeadLock();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
deadLock.addToList1("value-" + i);
}
}, "Thread-A");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
deadLock.addToList2("value-" + i);
}
}, "Thread-B");
t1.start();
t2.start();
}
}
死锁排查方案
1 使用jstack命令排查
# 1. 首先找到Java进程ID jps # 2. 使用jstack查看线程堆栈 jstack <PID> > thread_dump.txt # 3. 或者直接输出到控制台 jstack <PID>
2 jstack输出示例
当发生死锁时,jstack会在末尾显示"Found one Java-level deadlock":
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f6d3800a800 (object 0x000000075a229640, java.lang.Object),
which is held by "Thread-2"
"Thread-2":
waiting to lock monitor 0x00007f6d3800a800 (object 0x000000075a229630, java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at DeadLockDemo$1.run(DeadLockDemo.java:32)
- waiting to lock <0x000000075a229640> (a java.lang.Object)
- locked <0x000000075a229630> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread-2":
at DeadLockDemo$2.run(DeadLockDemo.java:56)
- waiting to lock <0x000000075a229630> (a java.lang.Object)
- locked <0x000000075a229640> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
3 使用jvisualvm图形化工具
# 启动JVisualVM jvisualvm
步骤:
- 在JVisualVM中双击需要查看的Java进程
- 选择"线程"标签页
- 如果有死锁,会看到红色提示
- 点击"线程转储"按钮可以获取详细的线程信息
4 使用jconsole监控工具
# 启动JConsole jconsole
步骤:
- 连接到需要监控的Java进程
- 切换到"线程"标签页
- 点击"检测死锁"按钮
5 自动化检测脚本
#!/bin/bash
# 死锁检测脚本 check_deadlock.sh
# 获取PID
PID=$1
if [ -z "$PID" ]; then
echo "Usage: ./check_deadlock.sh <PID>"
exit 1
fi
# 执行jstack并检查是否包含死锁关键字
JSTACK_OUTPUT=$(jstack $PID)
if echo "$JSTACK_OUTPUT" | grep -q "deadlock"; then
echo "警告: 检测到死锁!"
echo "========================================="
echo "$JSTACK_OUTPUT" | grep -A20 "deadlock"
else
echo "未检测到死锁"
fi
6 Spring Boot应用死锁排查
# application.yml 配置
management:
endpoints:
web:
exposure:
include: health,info,threaddump
访问:http://localhost:8080/actuator/threaddump
死锁排查步骤总结
- 检查日志: 查看应用日志中是否有超时或阻塞的提示
- 获取PID: 使用
jps或ps -ef | grep java找到Java进程ID - 执行线程转储: 使用
jstack <PID> -l获取详细线程信息 - 分析死锁: 找到线程堆栈中显示"Found one Java-level deadlock"的部分
- 分析代码: 根据堆栈信息定位到具体的代码行
- 确认资源分配: 查看哪些线程持有哪些锁,在等待哪些锁
可以采用以下命令快速排查:
# 1. jps查看所有Java进程 jps # 2. jstack转储线程并通过管道查找死锁 jstack <PID> | grep -A 10 "deadlock" # 3. 如果使用Linux还可以使用thread dump kill -3 <PID>
预防死锁的最佳实践
public class DeadLockPrevention {
// 方案1:统一锁的顺序
public void method1() {
synchronized (lockA) {
synchronized (lockB) {
// 业务逻辑
}
}
}
public void method2() {
synchronized (lockA) { // 和method1保持相同的锁顺序
synchronized (lockB) {
// 业务逻辑
}
}
}
// 方案2:使用tryLock方法
public void safeMethod() {
if (lockA.tryLock()) {
try {
if (lockB.tryLock()) {
try {
// 业务逻辑
} finally {
lockB.unlock();
}
}
} finally {
lockA.unlock();
}
}
}
// 方案3:使用并发工具类
private final ReentrantLock lock = new ReentrantLock();
public void concurrencySafeMethod() {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
// 业务逻辑
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
}
死锁分析工具总结
| 工具 | 命令 | 适用场景 |
|---|---|---|
| jstack | jstack <PID> |
命令行的快速排查 |
| JVisualVM | GUI工具 | 可视化分析,适合初学 |
| JConsole | GUI工具 | 实时监控和检测 |
| ThreadMXBean | 编程方式 | 应用内自动检测 |
| Java Flight Recorder | 性能分析 | 深入分析性能问题 |
使用编程方式检测死锁:
import java.lang.management.*;
public class DeadlockDetector {
public static void detectDeadlock() {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
if (deadlockedThreads != null) {
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads);
System.out.println("检测到死锁! 涉及的线程:");
for (ThreadInfo ti : threadInfos) {
System.out.println(ti.getThreadName());
}
}
}
}
通过以上方法和工具,可以有效的排查和解决Java程序中的死锁问题。