这篇文章的内容最初是由Tae Jin Gu在Cubrid博客上写的。

当Java程序运行存在障碍时,或者基于Java的Web应用程序运行速度比预期慢得多时,我们需要使用线程转储。如果您觉得线程转储非常复杂,本文可能会对您有很大帮助。在这里,我将解释Java中的线程是什么、它们的类型、如何创建它们、如何管理它们、如何从正在运行的应用程序中转储线程,以及最后如何分析它们并确定瓶颈或阻塞线程。本文是对Java应用程序调试的长期经验的总结。

Java和线程

你或许了解web服务器使用数十到数百个线程来处理大量并发用户。如果两个或多个线程使用相同的资源,线程之间的争用是不可避免的,有时会发生死锁。(死锁:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。)

线程争用是一个线程等待另一个线程持有的锁被解除的状态。不同的线程经常会同时访问web应用程序上的共享资源。例如,要记录日志,试图记录日志的线程必须获得一个锁并访问共享资源。

死锁是线程争用的一种特殊类型,其中两个或多个线程正在等待其他线程完成它们的任务,以便完成它们自己的任务。

线程争用可能引发不同的问题。要分析这些问题,您需要使用线程转储。线程转储将提供关于每个线程的确切状态的信息。

1、Java线程产生的条件

  • 线程同步

为了确保多个线程尝试使用共享资源时的兼容性,应该允许一次一个线程通过使用线程同步访问共享资源。

可以使用monitor在Java上实现线程同步。每个Java对象都有一个监视器。监视器只能由一个线程拥有。一个线程要拥有一个由另一个线程拥有的监视器,它需要在等待队列中等待,直到另一个线程释放它的监视器。

  • 线程状态

为了分析线程转储,您需要知道线程的状态,线程的状态是在java.lang.Thread.State上声明的。

  1. NEW: 已经创建了线程,但是还没有被处理。

  2. RUNNABLE :线程占用CPU并处理任务。(由于OS的资源分布,它可能处于等待状态。)

  3. BLOCKED:线程正在等待另一个线程释放它的锁,以便获得监视器锁。

  4. WAITING:等待:线程使用wait、join或park方法等待。

  5. TIMED_WAITING:线程使用sleep、wait、join或park方法等待。(与WAITING不同的是,最大等待时间由方法参数指定,等待可以通过时间和外部变化来缓解。)

2、Thread 类型

Java线程可以分为两个:

  1. 守护进程线程;

  2. 以及非守护进程线程。

当没有其他非守护进程线程时,守护进程线程停止工作。即使不创建任何线程,Java应用程序默认情况下也会创建多个线程。其中大多数是守护进程线程,主要用于处理垃圾收集或JMX等任务。

一个运行“static void main(String[] args)”方法的线程被创建为一个非守护进程线程,当这个线程停止工作时,所有其他守护进程线程也将停止。(运行此主方法的线程在HotSpot VM中称为VM线程。)

获取线程转储

我们将介绍三种最常用的方法。注意,还有许多其他方法可以获得线程转储。线程转储只能显示测量时的线程状态,因此,为了查看线程状态的变化,建议将其从5次提取到10次,间隔5秒。

1、使用jstack获取线程转储

在JDK 1.6或更高版本中,可以使用jstack在MS Windows上获得线程转储。

通过jps使用PID检查当前运行的Java应用程序进程的PID。

图1

将提取的PID作为jstack的参数,得到一个线程转储。

图2

2、使用jVisualVM的线程转储

(VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带(java启动时不需要特定参数,监控工具在bin/jvisualvm.exe),能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。

使用jVisualVM之类的程序生成线程转储。

使用visualvm的线程转储

左边的任务指示当前正在运行的进程列表。单击需要信息的进程,并选择thread选项卡实时检查线程信息。单击右上角的线程转储按钮获取线程转储文件。

3、在Linux终端中生成

使用ps -ef命令获取进程pid,检查当前运行的Java进程的pid

图3

线程转储文件中的线程信息

图4

  • 线程名:当使用Java.lang时。类来生成一个线程,该线程将被命名为Thread-(Number),而当使用java.util.concurrent时。它将被命名为池-(number)-线程-(number)。

  • 优 先 级:表示线程的优先级。

  • 线 程 ID:表示线程的唯一ID。(使用线程ID可以获得一些有用的信息,包括线程的CPU使用情况或内存使用情况。)

  • 线程状态:表示线程的状态。

  • 线程调用堆栈:表示线程的调用堆栈信息。

线程转储模式的类型

1、当无法获得锁(被阻塞)时

这是应用程序的整体性能下降的时候,因为一个线程占用了这个锁,并阻止其他线程获得它。在下面的示例中,BLOCKED_TEST pool-1-thread-1线程运行时带有<0x0000000780a000b0>锁,而BLOCKED_TESTpool-1-thread-2和BLOCKED_TEST pool-1-thread-3线程等待获得<0x0000000780a000b0>锁。

阻塞其他线程的线程

图5,图6

2、当处于死锁状态时

此时线程A需要获得线程B的锁来继续其任务,而线程B需要获得线程A的锁来继续其任务。在线程转储中,您可以看到DEADLOCK_TEST-1线程拥有0x00000007d58f5e48锁,并且正在尝试获得0x00000007d58f5e60锁。您还可以看到DEADLOCK_TEST-2线程拥有0x00000007d58f5e60锁,并且正在尝试获得0x00000007d58f5e78锁。此外,DEADLOCK_TEST-3线程拥有0x00000007d58f5e78锁,并试图获得0x00000007d58f5e48锁。正如您所看到的,每个线程都在等待获取另一个线程的锁,直到一个线程丢弃其锁,此状态才会更改。

处于死锁状态的线程

图7

3、当连续等待从远程服务器接收消息时

线程看起来是正常的,因为它的状态一直显示为RUNNABLE。然而,当您按时间顺序排列线程转储时,您可以看到socketReadThread线程正在无限地等待读取套接字。

持续的等待状态

图8

4、当Waiting

线程保持Waiting状态,在线程转储中,IoWaitThread线程一直等待接收来自LinkedBlockingQueue的消息。如果LinkedBlockingQueue仍然没有消息,那么线程状态将不会更改。

图9waiting status

5、当线程资源不能正常组织时

当线程资源不能正常组织时,不必要的线程就会堆积起来。如果发生这种情况,建议监视线程组织过程或检查线程终止的条件。

如何使用线程转储解决问题

例1:当CPU使用率异常高时

1. 提取CPU使用率最高的线程

图10

从应用程序中找出使用CPU最多的线程。

获取使用CPU最多的轻量级进程(LWP),并转换其惟一的编号(100)。

2. 获取线程转储之后,检查线程的操作。

提取PID为10029的应用程序的线程转储,然后找到nid为0的线程。

图11

每小时提取几次线程转储,并检查线程的状态更改以确定问题。

例2:当处理性能异常缓慢时

在多次获取线程转储之后,找到BLOCKED  状态的线程列表。

图12

在多次获得线程转储之后获取具有BLOCKED 状态的线程列表。

如果线程被阻塞,则提取与线程试图获取的锁相关的线程。

通过线程转储,您可以确认由于无法获得<0xe0375410>锁,线程状态保持BLOCKED 状态。这个问题可以通过分析当前持有锁的线程的堆栈跟踪来解决。

上面的模式经常出现在使用DBMS的应用程序中有两个原因:

第一个原因是配置不足。尽管这些线程仍在工作,但它们不能显示出最佳性能,因为DBCP等配置并不充分。如果您多次提取线程转储并比较它们,您将经常看到以前被阻塞的一些线程处于不同的状态。

第二个原因是连接不正常。当与DBMS的连接不正常时,线程将等待时间结束。在这种情况下,即使在多次提取线程转储并比较它们之后,您仍然会看到与DBMS相关的线程仍然处于阻塞状态。通过充分更改值,例如超时值,可以缩短问题发生的时间。

编写便于线程转储的代码

  • 线程命名

    当使用java.lang创建线程时。对象,该线程将被命名为Thread-(Number)。当使用java.util.concurrent创建线程时。对象,该线程将被命名为池-(Number)-线程-(Number)。当为一个应用程序分析成千上万个线程时,如果所有线程仍然有它们的默认名称,那么分析它们将变得非常困难,因为很难区分要分析的线程。

    因此,建议您养成每当新线程是c时为线程命名的习惯。

当您使用java.lang.Thread,创建线程时。线程,您可以使用creator参数为线程提供自定义名称。

图13

当您使用java.util.concurrent创建线程时。您可以通过生成自己的ThreadFactory来命名它。如果不需要特殊功能,可以使用MyThreadFactory。如下所述:

图14,图15

  • 通过使用MBean获得更详细的信息

您可以使用MBean获取ThreadInfo对象,您还可以通过使用ThreadInfo获得更多难以通过线程转储获得的信息。

图16

您可以使用ThreadInfo中的方法获取线程等待或阻塞的时间量,通过使用这个方法,您还可以获得异常长时间处于非活动状态的线程列表。

小结

在本文中,我关心的是,对于具有丰富多线程编程经验的开发人员来说,这些材料可能是常识,而对于经验较少的开发人员来说,我觉得我直接跳过了线程转储,没有提供足够的关于线程活动的背景信息。如果文中不懂的地方欢迎下方扫码关注留言,或许有你想要的信息呢。

长按二维码 ▲

订阅「架构师小秘圈」公众号

如有启发,帮我点个在看,谢谢↓

Java线程如何转储相关推荐

  1. java 线程转储_获取Java线程转储的常用方法(推荐)

    1. 线程转储简介 线程转储(Thread Dump)就是JVM中所有线程状态信息的一次快照. 线程转储一般使用文本格式, 可以将其保存到文本文件中, 然后人工查看和分析, 或者使用工具/API自动分 ...

  2. java 线程不足_jvm - 如何在没有运行缺点的Windows上获取Java进程的线程和堆转储...

    jvm - 如何在没有运行缺点的Windows上获取Java进程的线程和堆转储 我有一个Java应用程序,我从控制台运行,然后控制台执行另一个Java进程. 我想获得该子进程的线程/堆转储. 在Uni ...

  3. java 线程面试题_JAVA多线程面试题(一)

    1.进程和线程的区别 一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个任务.Java运行环境是一个包含了不同的类和程序的单一进 ...

  4. java线程死锁_Java并发:隐藏线程死锁

    java线程死锁 大多数Java程序员熟悉Java线程死锁概念. 它本质上涉及2个线程,它们彼此永远等待. 这种情况通常是平面(同步)或ReentrantLock(读或写)锁排序问题的结果. Foun ...

  5. 分析java 线程占用内存_Java线程:保留的内存分析

    分析java 线程占用内存 本文将为您提供一个教程,使您可以确定活动应用程序Java线程中保留了多少Java堆空间 . 将提供来自Oracle Weblogic 10.0生产环境的真实案例研究,以使您 ...

  6. java线程死锁_Java线程死锁–案例研究

    java线程死锁 本文将描述从在IBM JVM 1.6上运行的Weblogic 11g生产系统中观察到的最新Java死锁问题的完整根本原因分析. 此案例研究还将证明掌握线程转储分析技能的重要性: 包括 ...

  7. Java线程面试的前50个问题,面向初学者和经验丰富的程序员

    您可以参加任何Java面试,无论是大四还是中级,经验或新来的人,一定会看到线​​程,并发和多线程中的几个问题. 实际上,这种内置的并发支持是Java编程语言的最强优势之一,并帮助它在企业界和程序员中同 ...

  8. Java线程:保留的内存分析

    本文将为您提供一个教程,使您可以确定活动应用程序Java线程保留Java堆空间的数量和位置. 将提供来自Oracle Weblogic 10.0生产环境的真实案例研究,以使您更好地理解分析过程. 我们 ...

  9. 哪个Java线程消耗了我的CPU?

    当您的Java应用程序占用100%的CPU时,您该怎么办? 事实证明,您可以使用内置的UNIX和JDK工具轻松找到有问题的线程. 不需要探查器或代理. 为了进行测试,我们将使用以下简单程序: publ ...

最新文章

  1. 剑指offer-包含min函数的栈
  2. laravel5.6 mysql_快速入门 |《Laravel 5.6 中文文档 5.6》| Laravel China 社区
  3. 进程、线程、协程对比
  4. web文件管理系统_财务影像管理系统
  5. python3中exec_Python3
  6. jQuery入门 简单用法
  7. post请求需要加密吗_你需要吗?这款闪存盘不仅可以专业加密,还能云备份
  8. python课堂_python课堂整理6---字典
  9. 卡巴斯基提供升级包 解决病毒库升级
  10. 最大流(Maximum Flow)
  11. HTML学生个人网站作业设计——HTML+CSS+JavaScript简单的大学生书店网页制作(13页) web期末作业设计网页 web结课作业的源码 web网页设计实例作业
  12. 如何快速将MOV格式视频转换成MP4格式
  13. 多目标进化算法详细讲解及代码实现(样例:MOEA/D、NSGA-Ⅱ求解多目标(柔性)作业车间调度问题)
  14. axure 元件_Axure动态面板(上下左右滑动页面)
  15. ADAMoracle预言机将数据传至链上实现区块链落地应用
  16. 5G NR 基础原理与关键技术
  17. RuntimeError: CUDA error: CUBLAS_STATUS_NOT_INITIALIZED when calling `cublas‘
  18. java 面试108
  19. 常见的header头
  20. Feign的hystrix熔断器不起作用

热门文章

  1. [NC15034]德玛西亚万岁
  2. python 微信公众号回复图片_Python webpy微信公众号开发之 回复图文消息
  3. UVA1342 That Nice Euler Circuit(ACM - ICPC 2004 Asia - Shanghai)(计算几何、欧拉定理)
  4. typeof,instanceof 和 isProtoTypeof
  5. Codeforces Round #562 (Div. 2) B. Pairs
  6. 数据结构与算法 | 线性表 —— 链表
  7. puppet 连载二:服务端和客户端安装(ActiveMQ、MCollective)
  8. phpstorm 快捷键
  9. 8款审核AWS帐户安全性的免费工具,你值得拥有
  10. 《UX最佳实践:提高用户体验影响力的艺术 》一3.6 总结