Java死锁案例怎么排查规避

wen java案例 25

Java死锁案例排查与规避

死锁示例代码

public class DeadlockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    public static void main(String[] args) {
        // 线程1:先持有lock1,再获取lock2
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("线程1 持有 lock1");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("线程1 等待 lock2");
                synchronized (lock2) {
                    System.out.println("线程1 获取 lock2");
                }
            }
        });
        // 线程2:先持有lock2,再获取lock1
        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("线程2 持有 lock2");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                System.out.println("线程2 等待 lock1");
                synchronized (lock1) {
                    System.out.println("线程2 获取 lock1");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

死锁排查方法

使用jstack命令

# 1. 先找到Java进程ID
jps -l
# 2. 使用jstack分析线程堆栈
jstack -l <pid>

输出中会明确显示:

Java死锁案例怎么排查规避

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000001b3e4b28 (object 0x000000076b3e4b28, a java.lang.String),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000001b3e4b28 (object 0x000000076b3e4b28, a java.lang.String),
  which is held by "Thread-1"

使用JConsole图形化工具

# 启动JConsole
jconsole

步骤:

  1. 选择目标Java进程
  2. 点击"线程"标签
  3. 点击"检测死锁"按钮
  4. 查看死锁的线程及持有的锁

使用VisualVM

# 启动VisualVM
jvisualvm

步骤:

  1. 选择目标进程
  2. 进入"线程"选项卡
  3. 查看线程状态,死锁线程会显示为红色

代码层面检测

public class DeadlockDetector {
    public static void detectDeadlock() {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        long[] threadIds = threadBean.findDeadlockedThreads();
        if (threadIds != null) {
            System.out.println("检测到死锁!");
            ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds);
            for (ThreadInfo info : threadInfos) {
                System.out.println("死锁线程: " + info.getThreadName());
                System.out.println("等待的锁: " + info.getLockName());
                System.out.println("持有的锁: " + Arrays.toString(info.getLockedMonitors()));
            }
        }
    }
}

死锁规避策略

保持锁的顺序一致

public class SafeDeadlockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    // 使用统一的锁获取顺序
    public void safeMethod1() {
        synchronized (lock1) {
            synchronized (lock2) {
                // 业务逻辑
            }
        }
    }
    public void safeMethod2() {
        synchronized (lock1) {  // 保持相同顺序
            synchronized (lock2) {
                // 业务逻辑
            }
        }
    }
}

使用锁超时机制

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class TimeoutLockDemo {
    private final Lock lock1 = new ReentrantLock();
    private final Lock lock2 = new ReentrantLock();
    public void safeMethod() {
        try {
            // 尝试获取lock1,超时时间1秒
            if (lock1.tryLock(1, TimeUnit.SECONDS)) {
                try {
                    // 尝试获取lock2,超时时间1秒
                    if (lock2.tryLock(1, TimeUnit.SECONDS)) {
                        try {
                            // 业务逻辑
                            System.out.println("获取所有锁成功");
                        } finally {
                            lock2.unlock();
                        }
                    } else {
                        System.out.println("获取lock2超时,释放lock1");
                    }
                } finally {
                    lock1.unlock();
                }
            } else {
                System.out.println("获取lock1超时");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

使用Java并发工具类

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.StampedLock;
public class ConcurrentToolDemo {
    private final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
    private final StampedLock stampedLock = new StampedLock();
    public void safeMethod() {
        // 使用StampedLock的乐观读
        long stamp = stampedLock.tryOptimisticRead();
        try {
            // 业务逻辑
        } finally {
            if (!stampedLock.validate(stamp)) {
                // 转换为悲观读锁
                stamp = stampedLock.readLock();
                try {
                    // 重新读取
                } finally {
                    stampedLock.unlockRead(stamp);
                }
            }
        }
    }
}

使用数据库行级锁

// 使用数据库乐观锁避免死锁
public class DatabaseLockDemo {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void updateWithOptimisticLock(int id, int version) {
        String sql = "UPDATE user SET name = ?, version = version + 1 " +
                    "WHERE id = ? AND version = ?";
        int result = jdbcTemplate.update(sql, "newName", id, version);
        if (result == 0) {
            throw new OptimisticLockException("数据已被其他线程修改");
        }
    }
}

最佳实践建议

  1. 锁粒度控制:尽量使用细粒度锁,减少锁的持有范围
  2. 锁超时:使用tryLock()方法设置超时时间
  3. 统一锁顺序:所有线程按相同顺序获取锁
  4. 减少锁的嵌套:尽量避免在持有锁的情况下获取其他锁
  5. 使用原子操作:如AtomicIntegerConcurrentHashMap
  6. 定期检测:使用监控工具定期检查死锁情况
  7. 代码审查:特别是涉及多线程锁的代码

死锁检测工具对比

工具 优点 缺点 适用场景
jstack 命令行,快速 需要知道PID 线上紧急排查
JConsole 图形化,直观 需要GUI环境 开发/测试环境
VisualVM 功能丰富 启动较慢 性能调优
Java代码检测 可自动化 需要额外编码 监控系统集成

通过以上方法,可以有效排查和规避Java死锁问题。

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