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 等很多工具都可以检查死锁。
在实际应用中,死锁的定位可能会比较复杂。可以通过以下过程排查:
- 检查区分线程状态
- 查看等待的目标
- 对比 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 非常容易出问题。
- 如果必须用多个锁,需仔细设计推演锁的获取顺序。让所有线程按照相同的顺序获取锁,就是一种常见的思路。
- 使用带超时的方法。可以让程序假定可能无法获得锁,并设定锁获取失败时的退出逻辑。
相关推荐
CmBacktrace模块能记录并输出调用栈信息,对于排查死锁、栈溢出等问题非常有帮助。 6. syslog模式 3.5.1 syslog配置 ULOG支持syslog协议,可以将日志发送到远程syslog服务器,便于集中管理和分析。 3.5.2 日志...
Java 多线程之死锁 Java 多线程中的死锁是指两个或两个以上的...死锁是 Java 多线程中的一种常见的问题,它可以通过加锁顺序和加锁时限来避免。同时,我们也可以使用一些工具来排查问题,例如 jps 和 jstack 命令。
6. **线程监控**:展示应用中的活动线程,有助于排查死锁和阻塞问题。 7. **系统信息**:显示服务器的CPU使用率、操作系统信息等。 `jrobin-1.5.9.jar`则是JavaMelody用来存储监控数据的库。JRobin是JFreeChart项目...
`show-busy-java-threads.sh`脚本就是为了帮助开发者快速定位和排查这类性能问题而设计的。这个脚本主要用于监控并展示Java应用程序中的繁忙线程,从而帮助我们理解程序的执行状态,找出可能导致高CPU负载的原因。 ...
在IT领域,线程堆栈分析是排查性能问题和故障诊断的重要手段,特别是在大型企业级应用中,如Java应用程序。IBM提供了几个实用工具,如jca436和jca461,来帮助开发者和运维人员深入理解线程状态和定位问题。本篇文章...
Java线程分析是Java开发中的重要环节,尤其是在处理性能优化、死锁排查或者并发问题时。TDA(Thread Dump Analyzer)是一款强大的Java线程分析工具,它能够帮助开发者深入理解应用在运行时的线程状态,包括线程的...
了解了这些基本机制后,我们可以通过`jconsole`、`jvisualvm`等工具进行线程监控,查看线程状态、死锁检测等,辅助排查和解决问题。 在实际开发中,我们还需要遵循一些最佳实践,比如避免长时间持有锁、减少同步...
### Java开发进阶知识点概述 #### 一、多线程深入理解 在Java开发中,多线程技术是一项非常重要的技能。它可以帮助开发者构建出高效、响应迅速的应用程序。接下来,我们将详细介绍多线程的基本概念及其高级应用。 ...
在排查死锁时,开发者需要结合日志、代码审查以及这类工具进行综合分析。使用LockCop这样的工具可以大大简化这个过程,提高问题定位的效率。在实际操作中,我们需要根据LockCop提供的信息,检查代码中是否有不当的...
Java虚拟机(JVM)是Java程序运行的基础,它负责管理程序的内存、线程以及类加载等核心功能。在开发和运维过程中,有时会遇到Java应用的性能问题,如线程CPU占用过高或者线程死锁。这时,我们就需要用到JVM提供的...
- **线程dump**:jstack命令获取线程状态,排查死锁、阻塞等问题。 - **日志分析**:JVM会生成各种日志,如gc.log,用于分析垃圾收集行为。 6. **性能瓶颈识别** - **CPU耗时分析**:找出消耗CPU最多的代码段。 ...
在Java编程中,线程是程序执行的基本单元,它允许应用程序同时执行多个任务。然而,线程在运行过程中可能会遇到各种错误和异常,如死锁、竞态条件、空指针异常等,这些都需要程序员有效地捕获和处理。"CheckThread...
《专业Java JDK - 第5版》是一本专为Java开发者深度解析JDK的权威指南,其第五版在2005年出版,旨在帮助读者掌握Java开发的核心技术和最新进展。这本书详细介绍了Java语言的各个方面,涵盖了从基础语法到高级特性的...
- **线程监控**:查看应用中的活动线程和死锁,有助于排查多线程问题。 - **会话管理**:统计在线用户数量和会话信息,便于了解用户行为。 - **异常跟踪**:记录和显示应用程序中的异常信息,方便故障排查。 - *...
- **jstack**:打印线程堆栈信息,用于排查死锁和线程阻塞问题。 通过深入学习和理解这些JVM知识点,开发者可以更好地优化Java程序,提高性能,解决运行时问题,并进一步提升编程技能。在实际开发中,了解JVM的...
本文将深入探讨Java线上故障分析的关键技术之一——线程dump与堆内存分析,帮助开发者快速定位并解决问题。 #### 线程dump解析 **线程dump**是一种用于捕捉程序运行时刻所有线程状态的快照,它能够提供关于线程的...
`show-busy-java-threads.sh` 文件提供了一个实用的脚本,帮助我们查看那些导致Linux系统CPU占用率升高的Java线程。 首先,让我们了解什么是JVM。JVM(Java Virtual Machine)是Java程序的运行环境,它负责解释和...
《多线程编程实战指南-核心篇》是针对Java开发者深入理解并掌握多线程编程的一本实战性书籍。在当今的并发计算环境中,多线程技术是必不可少的知识点,它能够有效地利用多核处理器资源,提高程序的执行效率。本书以...
线程同步问题、死锁、活锁等都可能引发难以排查的问题。学会使用线程堆栈信息、线程挂起与恢复等技巧,能有效解决这类问题。 异常处理是另一个重要的调试主题。理解和利用异常堆栈跟踪,可以迅速定位到错误发生的...