一文详解查看java死锁具体怎么做以及怎么避免

 更新时间:2025年10月27日 09:00:01   作者:JanelSirry  
死锁的产生源于资源争夺的无序性,其核心是四大必要条件的同时满足,这篇文章主要介绍了查看java死锁具体怎么做以及怎么避免的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

在 Java 应用中,死锁(Deadlock) 是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,导致这些线程都无法继续执行下去,程序“卡死”。这是一种非常典型的并发问题。

一、什么是 Java 死锁?

死锁产生的四个必要条件(经典条件,缺一不可):

  1. 互斥条件:资源一次只能由一个线程占用。
  2. 占有并等待:线程持有至少一个资源,并等待获取其他被占用的资源。
  3. 非抢占条件:线程已获得的资源在未使用完之前不能被其他线程强行夺取,必须自己释放。
  4. 循环等待条件:存在一个线程的循环等待链,每个线程都在等待下一个线程所占用的资源。

二、如何查看 / 检测 Java 中的死锁?

Java 提供了多种方式来 检测死锁,下面介绍几种 最常用、最实用的方法

方法一:使用jstack 工具查看死锁(推荐,简单有效)

步骤:

  1. 找到 Java 应用的进程 ID(PID)

    在 Linux / macOS 终端 或 Windows 的命令行下运行:

    jps
    

    输出类似:

    12345 MyApp
    6789  Jps
    

    其中 12345 就是你的 Java 应用的进程ID(PID)。

    你也可以使用 ps -ef | grep java(Linux/macOS)或任务管理器(Windows)查找。

  2. 使用 jstack 查看线程堆栈,并检测死锁

    执行:

    jstack <PID>
    

    例如:

    jstack 12345
    
  3. 在输出内容中搜索关键字:deadlock 或 Found one Java-level deadlock

    如果存在死锁,jstack 会自动检测并在输出中明确标识出来,例如:

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

    它会清楚地告诉你哪些线程在互相等待哪些锁,从而形成死锁环。

🔍 小技巧:

  • 如果输出内容太多,可以将结果重定向到文件查看:
    jstack 12345 > thread_dump.txt
    
    然后打开 thread_dump.txt 搜索 deadlockdead-lock

方法二:使用jconsole 或 VisualVM 图形化工具查看死锁

这些是 Java 自带的 GUI 工具,可以直观地查看线程状态和死锁。

1.jconsole(JDK 自带)

  • 启动方式:
    jconsole
    
  • 在弹出的界面中,选择你的 Java 进程;
  • 切换到 “线程” 标签页;
  • 点击 “检测死锁”(Detect Deadlock) 按钮;
  • 如果存在死锁,它会列出死锁涉及的线程和锁信息。

2.VisualVM(JDK 自带,功能更强大)

  • 启动方式:
    jvisualvm
    
  • 选择你的 Java 进程;
  • 切换到 Threads(线程) 标签;
  • 如果存在死锁,会有明确的提示,也可以看到线程互相等待的情况;
  • 也可以使用插件增强功能(如 Visual GC 等)。

🧠 这两个工具适合开发和测试环境,不需要额外安装,JDK 中自带有。

方法三:在代码中编程检测死锁(高级用法,不常用)

Java 提供了一个 ThreadMXBean 接口,可以 以编程方式检测死锁

示例代码:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;

public class DeadlockDetector {
    public static void main(String[] args) {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();

        if (deadlockedThreads != null && deadlockedThreads.length > 0) {
            System.err.println("检测到死锁!涉及以下线程:");
            for (long threadId : deadlockedThreads) {
                java.lang.management.ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId);
                System.err.println(threadInfo.getThreadName() + " 正在等待锁,被 " +
                        threadInfo.getLockOwnerName() + " 持有");
            }
        } else {
            System.out.println("未检测到死锁。");
        }
    }
}

你可以定期调用这个检测逻辑(比如通过定时任务),或在监控系统中集成,用于自动化检测死锁。

三、如何避免和解决死锁?

检测到死锁不是终点,关键还是要 避免死锁发生,以下是常见的 死锁预防 / 解决策略

1.避免嵌套锁 / 按固定顺序获取锁

死锁常常发生在多个线程以不同顺序获取多个锁时。

解决方案:

  • 所有线程按照相同的顺序获取锁,例如总是先获取锁A,再获取锁B,不要有的线程先A后B,有的先B后A。

🔒 反面例子(容易死锁):

// 线程1:锁A -> 锁B
// 线程2:锁B -> 锁A
synchronized(lockA) {
    synchronized(lockB) { ... }
}

正确做法:统一顺序,如都先获取 lockA,再 lockB

2.使用锁超时机制

使用如 ReentrantLock.tryLock(long timeout, TimeUnit unit) 方法,尝试获取锁,如果在指定时间内获取不到,就放弃,避免一直等待。

示例:

if (lock.tryLock(1, TimeUnit.SECONDS)) {
    try {
        // 执行业务
    } finally {
        lock.unlock();
    }
} else {
    // 获取锁失败,做相应处理,避免死等
}

3.减少同步代码块的范围

  • 只对必要的代码加锁,尽快释放锁;
  • 避免在持有多个锁的情况下执行耗时操作(如 IO、网络请求等)。

4.使用更高级的并发工具替代 synchronized

  • 如使用 java.util.concurrent 包中的工具类ReentrantLockSemaphoreCountDownLatchConcurrentHashMap 等;
  • 这些工具更灵活,有些支持超时、中断等机制,能更好地避免死锁。

四、总结:如何查看 Java 死锁(快速版)

方法工具/命令说明是否需要重启/侵入代码
jstack命令行工具 jstack <pid>查看线程 dump,自动检测死锁,搜索 deadlock 关键字❌ 不需要,最常用
jconsoleJDK 自带 GUI 工具图形化界面,点击“检测死锁”按钮❌ 不需要
VisualVMJDK 自带 GUI 工具更强大的线程和内存监控,支持死锁检测❌ 不需要
编程检测ThreadMXBean.findDeadlockedThreads()可编程检测死锁,适合嵌入监控系统✅ 需写代码
日志与监控结合 APM 工具(如 SkyWalking、Arthas)生产环境推荐结合工具持续监控✅ 可选

推荐做法(最佳实践):

  1. 开发阶段:

    • 避免随意嵌套 synchronized 或多个锁;
    • 使用 jstack 定期检查线程状态,特别是在高并发测试时;
    • 使用工具如 jconsole / VisualVM 实时观察线程情况。
  2. 生产环境:

    • 通过 jstack 定时抓取线程 dump(比如通过脚本或监控工具);
    • 结合 APM 工具(如 Arthas、SkyWalking、Prometheus + Grafana)实时监控死锁和线程阻塞;
    • 发现死锁后,通过日志定位代码,优化锁策略。

到此这篇关于查看java死锁具体怎么做以及怎么避免的文章就介绍到这了,更多相关查看java死锁及避免内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot+Redis实现不重复消费的队列的示例代码

    SpringBoot+Redis实现不重复消费的队列的示例代码

    本文主要介绍了SpringBoot+Redis实现不重复消费的队列的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-07-07
  • Mybatis中返回Map的实现

    Mybatis中返回Map的实现

    这篇文章主要介绍了Mybatis中返回Map的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 使用Java获取List交集数据的实现方案小结

    使用Java获取List交集数据的实现方案小结

    今天遇到一个小需求,当用户上传了一个关于用户数据的列表,我们需要将其与数据库中已有的用户数据进行比较,所以本文给大家介绍了使用Java获取List交集数据的实现方案小结,文中有详细的代码示例供大家参考,需要的朋友可以参考下
    2024-03-03
  • Java日常练习题,每天进步一点点(56)

    Java日常练习题,每天进步一点点(56)

    下面小编就为大家带来一篇Java基础的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以帮到你
    2021-08-08
  • MyBatis-Plus中更新操作的两种实现

    MyBatis-Plus中更新操作的两种实现

    本文主要介绍了MyBatis-Plus中更新操作的两种实现,主要是通过id更新和条件更新,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • Fluent Mybatis零xml配置实现复杂嵌套查询

    Fluent Mybatis零xml配置实现复杂嵌套查询

    本文主要介绍了Fluent Mybatis零xml配置实现复杂嵌套查询,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Netty源码解析NioEventLoop创建的构造方法

    Netty源码解析NioEventLoop创建的构造方法

    这篇文章主要介绍了Netty源码解析NioEventLoopGroup之NioEventLoop创建的构造方法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03
  • 浅谈Java垃圾回收机制

    浅谈Java垃圾回收机制

    这篇文章主要介绍了浅谈Java垃圾回收机制,文中有非常详细的图文示例及代码示例,对正在学习java的小伙伴们有很好的帮助,需要的朋友可以参考下
    2021-05-05
  • Mybatis实现分表插件

    Mybatis实现分表插件

    随着系统的发展,数据量也会越来越大,分库分表可以有效的缓解数据库的压力,本文主要介绍了Mybatis实现分表插件,感兴趣的可以了解一下
    2021-05-05
  • 详解java之redis篇(spring-data-redis整合)

    详解java之redis篇(spring-data-redis整合)

    本篇文章主要介绍了java之redis篇,主要详细的介绍了spring-data-redis整合,有兴趣的可以了解一下。
    2017-01-01

最新评论