背景:最近测试批量任务执行的时候发现服务器端的线程数时不时的会出现飙升,而且执行完以后不见降低。要知道出现多线程问题是比较头疼的,由于有时候一味的跟代码不必定能看出问题,还比较耗时。这时候就须要用到一些工具来监控线程的执行过程和状态,JDK自身提供了提供了不少工具来监控jvm运行状态,这些工具都放在bin目录下。这里主要使用的是jvisualvm工具来监控线程,并经过导出的线程dump文件分析问题所在。java
jvisualvm打开是一个图形化界面linux
如图所示,该软件能够监控本地的java进程的CPU,类,线程,的消耗状况,点击线程dump也能够比较方便的查看当前的dump日志。固然还有其余功能也支持插件暂时没涉到,之后慢慢摸索。
windows
能够看到有Local和Remote两种方式打开,这里介绍下用SecureCRT远程链接Linux 使用X11 转发功能打开图形化窗口:bash
1.检查/etc/ssh/sshd_config文件,确保如下参数正确:服务器
AllowTcpForwarding yes
##启用X11
ForwardingX11Forwarding yes
X11UseLocalhost no复制代码
2.重启sshd服务:多线程
service sshd restart复制代码
3.检查下linux服务器上是否安装了xorg-x11-xauth,若是没有则须要先安装xauth,命令以下:ssh
yum install xorg-x11-xauth复制代码
4.在windows主机安装Xming, 启动X serverjvm
下载地址:工具
sourceforge.net/projects/xm…
下载安装完直接启动就行。post
5.安装SecureCRT,并配置对应会话的转发X11
SecureCRT有绿色版直接解压打开便可,配置对应会话的转发X11以下图:
注意:配置完以后要从新打开会话链接。
6.在SecureCRT创建的Linux会话界面cd到jdk安装路径的bin目录下直接启动
./jvisualvm复制代码
经过分析下载下来的dump文件发现出现大量的相似如下的信息:
"pool-16-thread-55" #767 prio=5 os_prio=0 tid=0x00007f4a90035000 nid=0x13e6 waiting on condition [0x00007f4960eb8000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000005cfbdc670> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None复制代码
大体介绍下上面的线程info信息快的内容
1. "pool-16-thread-55" #767 prio=5 os_prio=0 tid=0x00007f4a90035000 nid=0x13e6 waiting on condition [0x00007f4960eb8000]
# 线程名称:pool-16-thread-55;优先级: 5,默认是5;# JVM线程id:tid=0x00007f4a90035000,JVM内部线程的惟一标识(经过java.lang.Thread.getId()获取,一般用自增方式实现)。# 对应系统线程id(NativeThread ID):nid=0x13e6 ,和top命令查看的线程pid对应,不过一个是10进制,一个是16进制。(经过命令:top -H -p pid,能够查看该进程的全部线程信息)# 线程状态:waiting on condition
# 起始栈地址:[0x00007f4960eb8000],对象的内存地址,经过JVM内存查看工具,可以看出线程是在哪儿个对象上等待;2.java.lang.Thread.State: WAITING (parking)
3.at sun.misc.Unsafe.park(Native Method)
4.- parking to wait for <0x00000005cfbdc670> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
5.at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
6.at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)--等待任务进入
7.at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
8.at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)--获取执行任务
9.at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)--线程池运行
10.at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
11.at java.lang.Thread.run(Thread.java:748)复制代码
由上面线程信息块能够发现的出结论大量的线程都处于waiting状态。而且引发waiting的缘由是由于线程到任务队列获取任务时没获取到任务,致使线程被park住。也就是任务都执行完了可是线程还没被回收,缘由多是建立线程池时设置的核心线程数过大;也有可能建立了多个线程池实例,每次使用不一样的线程池实例运行任务致使。接下来就是去对应的地方分析代码实现(ps:看别人的代码是真是一个痛苦的过程),经过分析代码发现每一个服务都建立了对应的线程池去批量处理,并且每次添加新流程发布的时候都会从新建立一个新的流程线程池去执行服务。到这里问题的缘由找到了,接下来就是修改代码了。
分析线程dump文件时对于线程状态和为何会产生该状态的缘由要有必定的了解,否则即便有了线程信息也会一脸懵逼的。
具体的线程状态和产生缘由能够参考下面的文章