`

【Java核心-进阶】线程——排查死锁、避免死锁

    博客分类:
  • Java
 
阅读更多

1. 线程死锁的原因

死锁:两个或多个线程之间,由于互相持有对方需要的锁,而永久出于阻塞的状态



 

会引起死锁的代码示例:

public static void main(String[] args) throws InterruptedException {
  String lockA = "lock-A";
  String lockB = "lock-B";

  Thread t1 = new Thread(()->run(lockA, lockB), "Sample-Thread-A");
  Thread t2 = new Thread(()->run(lockB, lockA), "Sample-Thread-B");

  t1.start();
  t2.start();
  t1.join();
  t2.join();
}

static void run(String name, String firstLock, String secondLock) {
  String threadName = Thread.currentThread.getName();
  synchronized (firstLock) {
    System.out.println(String.format("%s obtained: %s", threadName, firstLock));
    try {
      Thread.sleep(1000);
      synchronized (secondLock) {
        System.out.println(String.format("%s obtained: %s", threadName, secondLock));
      }
    } catch (InterruptedException e) {
      // 忽略异常
    }
  }
}

 

2. 查找定位死锁

死锁的排查定位往往非常费时费力,不要对“快速解决方案”抱有幻想!

 

2.1 通过辅助工具定位死锁

jstack、jConsole 等很多工具都可以检查死锁。

在实际应用中,死锁的定位可能会比较复杂。可以通过以下过程排查:

    1. 检查区分线程状态
    2. 查看等待的目标
    3. 对比 Monitor 等持有状态

以上述示例程序为例,程序在运行过程中,我们可以:

 

# 通过命令 “jstack <pid>” 发现死锁信息:
 

 

# 通过 jConsole 中检测到此死锁:



 

2.2 通过 ThreadMXBean 定位死锁

如果是开发自己的管理工具,也可以在代码中使用 ThreadMXBean 来检测死锁。
注意:对线程进行快照是一个相对重量级的操作,须慎重选择频率和时机!

ThreadMXBean mBean = ManagementFactory.getThreadMXBean();
Runnable dlCheck = () -> {
  long[] threadIds = mBean.findDeadlockedThreads();
  if (threadIds != null) {
    ThreadInfo[] threadInfos = mBean.getThreadInfo(threadIds);
    System.out.println("Detected deadlock threads:");
    for (ThreadInfo threadInfo : threadInfos) {
      System.out.println(threadInfo.getThreadName());
    }
  }
};

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(dlCheck, 5, 10, TimeUnit.SECONDS);

 

2.3 排查死循环引起的“假死锁”

如果线程进入死循环,它可能被误认为发生了死锁。但是通过前述的方法又发现没有死锁。
这种情况下,死循环线程的 CPU使用率 会飙升。
所以可以查看CPU使用率,找到CPU占用率高的线程,输出线程堆栈信息,排查代码

以一个Linux环境中的Java程序为例:

a. 通过 top 命令找出CPU占用率较高的进程。

在运维管理合理的实际项目中,我们通常会事先将业务进程的 pid 记录到某个文件,无需额外查找。
配合容器化、微服务的部署,一个容器或一台(虚拟)服务器上不应运行多个业务进程。
而此处 top命令主要是从多个业务进程中找到那个因死循环而CPU占用率超高的进程。

 

此示例中,目标Java进程的进程号为 24310

 

b. 通过 ps 命令找出Java进程中CPU占用率较高的线程。

ps -mp 24310 -o THREAD,tid
找到对应线程的线程号为 24311

 

c. 通过 jstack 输出线程信息,对照上述线程号,找到对应线程堆栈信息,结合代码排查问题。

jstack 24310
jstack 输出的信息中,线程号是以16进制的格式展示的,所以上述线程号 24311 被显示为 0x5ef7
可通过命令 printf "%x\n" 24311 进行转换。

 

 

3. 避免死锁

产生死锁的基本要素:

  • 互斥条件。锁是独占式,同一时刻只能由单个线程持有。
  • 互斥条件(锁)被长期持有。在使用结束之前,线程不会自己释放锁,也不能被其它线程抢占。
  • 循环依赖。两个或多个线程之间形成锁的链条环。

根据死锁的基本要素避免死锁:

  • 尽量避免使用多个锁。嵌套的 synchronized 和 lock 非常容易出问题。
  • 如果必须用多个锁,需仔细设计推演锁的获取顺序。让所有线程按照相同的顺序获取锁,就是一种常见的思路。
  • 使用带超时的方法。可以让程序假定可能无法获得锁,并设定锁获取失败时的退出逻辑

 

  • 大小: 30.8 KB
  • 大小: 123.5 KB
  • 大小: 16.8 KB
  • 大小: 7.3 KB
  • 大小: 44 KB
  • 大小: 7.5 KB
  • 大小: 26.3 KB
分享到:
评论

相关推荐

    AN0024-RT-Thread-ulog 日志组件应用笔记 - 进阶篇1

    CmBacktrace模块能记录并输出调用栈信息,对于排查死锁、栈溢出等问题非常有帮助。 6. syslog模式 3.5.1 syslog配置 ULOG支持syslog协议,可以将日志发送到远程syslog服务器,便于集中管理和分析。 3.5.2 日志...

    Java多线程之-死锁.doc

    Java 多线程之死锁 Java 多线程中的死锁是指两个或两个以上的...死锁是 Java 多线程中的一种常见的问题,它可以通过加锁顺序和加锁时限来避免。同时,我们也可以使用一些工具来排查问题,例如 jps 和 jstack 命令。

    JavaMelody javamelody-core-1.52.0.jar jrobin-1.5.9.jar

    6. **线程监控**:展示应用中的活动线程,有助于排查死锁和阻塞问题。 7. **系统信息**:显示服务器的CPU使用率、操作系统信息等。 `jrobin-1.5.9.jar`则是JavaMelody用来存储监控数据的库。JRobin是JFreeChart项目...

    show-busy-java-threads.sh文件

    `show-busy-java-threads.sh`脚本就是为了帮助开发者快速定位和排查这类性能问题而设计的。这个脚本主要用于监控并展示Java应用程序中的繁忙线程,从而帮助我们理解程序的执行状态,找出可能导致高CPU负载的原因。 ...

    imb-jca-线程堆栈.zip

    在IT领域,线程堆栈分析是排查性能问题和故障诊断的重要手段,特别是在大型企业级应用中,如Java应用程序。IBM提供了几个实用工具,如jca436和jca461,来帮助开发者和运维人员深入理解线程状态和定位问题。本篇文章...

    java线程分析工具TDA

    Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...

    Java多线程 - (一) 最简单的线程安全问题

    了解了这些基本机制后,我们可以通过`jconsole`、`jvisualvm`等工具进行线程监控,查看线程状态、死锁检测等,辅助排查和解决问题。 在实际开发中,我们还需要遵循一些最佳实践,比如避免长时间持有锁、减少同步...

    圣思园张龙java开发进阶视频网盘.txt

    ### Java开发进阶知识点概述 #### 一、多线程深入理解 在Java开发中,多线程技术是一项非常重要的技能。它可以帮助开发者构建出高效、响应迅速的应用程序。接下来,我们将详细介绍多线程的基本概念及其高级应用。 ...

    死锁检测工具LockCop.zip

    在排查死锁时,开发者需要结合日志、代码审查以及这类工具进行综合分析。使用LockCop这样的工具可以大大简化这个过程,提高问题定位的效率。在实际操作中,我们需要根据LockCop提供的信息,检查代码中是否有不当的...

    JVM---jstack分析Java线程CPU占用,线程死锁的解决

    Java虚拟机(JVM)是Java程序运行的基础,它负责管理程序的内存、线程以及类加载等核心功能。在开发和运维过程中,有时会遇到Java应用的性能问题,如线程CPU占用过高或者线程死锁。这时,我们就需要用到JVM提供的...

    Java虚拟机-jvm故障诊断与性能优化-源码

    - **线程dump**:jstack命令获取线程状态,排查死锁、阻塞等问题。 - **日志分析**:JVM会生成各种日志,如gc.log,用于分析垃圾收集行为。 6. **性能瓶颈识别** - **CPU耗时分析**:找出消耗CPU最多的代码段。 ...

    基于Java的实例源码-线程错误捕获工具 CheckThread.zip

    在Java编程中,线程是程序执行的基本单元,它允许应用程序同时执行多个任务。然而,线程在运行过程中可能会遇到各种错误和异常,如死锁、竞态条件、空指针异常等,这些都需要程序员有效地捕获和处理。"CheckThread...

    Professional Java Jdk - 5th Edition

    《专业Java JDK - 第5版》是一本专为Java开发者深度解析JDK的权威指南,其第五版在2005年出版,旨在帮助读者掌握Java开发的核心技术和最新进展。这本书详细介绍了Java语言的各个方面,涵盖了从基础语法到高级特性的...

    javamelody-1.43.0

    - **线程监控**:查看应用中的活动线程和死锁,有助于排查多线程问题。 - **会话管理**:统计在线用户数量和会话信息,便于了解用户行为。 - **异常跟踪**:记录和显示应用程序中的异常信息,方便故障排查。 - *...

    22 深入拆解 Java 虚拟机-20200406T052847Z-001.zip

    - **jstack**:打印线程堆栈信息,用于排查死锁和线程阻塞问题。 通过深入学习和理解这些JVM知识点,开发者可以更好地优化Java程序,提高性能,解决运行时问题,并进一步提升编程技能。在实际开发中,了解JVM的...

    java线上故障分析-线程dump,堆内存分析

    本文将深入探讨Java线上故障分析的关键技术之一——线程dump与堆内存分析,帮助开发者快速定位并解决问题。 #### 线程dump解析 **线程dump**是一种用于捕捉程序运行时刻所有线程状态的快照,它能够提供关于线程的...

    show-busy-java-threads-jvm-cpu.rar

    `show-busy-java-threads.sh` 文件提供了一个实用的脚本,帮助我们查看那些导致Linux系统CPU占用率升高的Java线程。 首先,让我们了解什么是JVM。JVM(Java Virtual Machine)是Java程序的运行环境,它负责解释和...

    多线程编程实战指南-核心篇

    《多线程编程实战指南-核心篇》是针对Java开发者深入理解并掌握多线程编程的一本实战性书籍。在当今的并发计算环境中,多线程技术是必不可少的知识点,它能够有效地利用多核处理器资源,提高程序的执行效率。本书以...

    JAVA高端进阶开发课程 JAVA应用程序调试技术 从实战角度出发学习JAVA应用程序调试.rar

    线程同步问题、死锁、活锁等都可能引发难以排查的问题。学会使用线程堆栈信息、线程挂起与恢复等技巧,能有效解决这类问题。 异常处理是另一个重要的调试主题。理解和利用异常堆栈跟踪,可以迅速定位到错误发生的...

Global site tag (gtag.js) - Google Analytics