Java死锁案例与排查

wen java案例 2

本文目录导读:

Java死锁案例与排查

  1. 死锁案例代码
  2. 更复杂的死锁案例(包含多个锁和嵌套调用)
  3. 死锁排查方案
  4. 死锁排查步骤总结
  5. 预防死锁的最佳实践
  6. 死锁分析工具总结

我为您提供一个完整的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

步骤:

  1. 在JVisualVM中双击需要查看的Java进程
  2. 选择"线程"标签页
  3. 如果有死锁,会看到红色提示
  4. 点击"线程转储"按钮可以获取详细的线程信息

4 使用jconsole监控工具

# 启动JConsole
jconsole

步骤:

  1. 连接到需要监控的Java进程
  2. 切换到"线程"标签页
  3. 点击"检测死锁"按钮

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

死锁排查步骤总结

  1. 检查日志: 查看应用日志中是否有超时或阻塞的提示
  2. 获取PID: 使用jpsps -ef | grep java找到Java进程ID
  3. 执行线程转储: 使用jstack <PID> -l获取详细线程信息
  4. 分析死锁: 找到线程堆栈中显示"Found one Java-level deadlock"的部分
  5. 分析代码: 根据堆栈信息定位到具体的代码行
  6. 确认资源分配: 查看哪些线程持有哪些锁,在等待哪些锁

可以采用以下命令快速排查:

# 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程序中的死锁问题。

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