扫描二维码,报名慧测免费公开课

下面通过一个实际的例子对线程堆栈的解读进行详细介绍。 掌握了线程堆栈解读的方法,就可以庖丁解牛, 对线程堆栈进行深入剖析。
从本章开始,希望你可以耐住性子认真看、多实践,收获必定会很大,开始!
如何解读Java线程堆栈
直接上实例代码:

上面代码的函数调用关系:main——》method1——》method2——》System.out.println()
其中:在method1里面对obj1对象加锁,在method2里面对obj2对象加锁。
运行该程序: java HCTest,使用Jvisualvm监控一下,可以看到如下线程状态图(我不喜欢看大量代码的文章,估计你也一样,看起来太费劲,想看详细的堆栈信息自己dump一下吧):

在上图中,可以看到总共产生了10个线程,其中只有main线程属于Java用户线程, 其它七个都是由虚拟机自动创建的;我们在实际分析的过程中, 只关心Java用户线程即可。展示一下main线程的堆栈信息:

从上面的main线程中看, 线程堆栈里面的最直观的信息是当前线程的调用上下文, 即从哪个函数中调用到哪个函数中( 从下往上看), 正执行到哪个包的哪个类的哪一行, 借助这些信息, 我们就对当前系统正在做什么就一目了然。
其中一个线程的某一层调用含义如下:

另外, 从main线程的堆栈中, 有"- locked <0x0b1633c8> (a java.lang.Object)"语句,这表示该线程(即main线程)已经占有了锁<0x0b1633c8>, 其中0x0b1633c8表示锁ID, 这个锁的ID是系统自动产生的, 我们只需要知道每次打印的堆栈, 同一个ID表示是同一个锁即可。 每一个线程堆栈的第一行含义如下:
关于锁的知识后面会专门分享,现在先知道这回事就行。

Java线程和本地线程
其中"线程对应的本地线程id号"所指的"本地线程"是指该Java线程所对应的虚拟机中的本地线程。 我们知道Java是解析型语言,执行的实体是Java虚拟机, 因此Java语言中的线程是依附于Java虚拟机中的本地线程来运行的, 实际上是本地线程在执行Java线程代码。
Java代码中创建一个thread, 虚拟机在运行期就会创建一个对应的本地线程, 而这个本地线程才是真正的线程实体。也就是说一个 Java 线程的创建根本上就对应了一个本地线程(native thread)的创建,两者是一一对应的。
可以使用如下方式产生java的本地线程信息:
使用ps -ef | grep java 获得Java进程ID。
使用pstack <java pid>获得Java虚拟机的本地线程的堆栈
pstack命令产生的本地线程堆栈信息摘取如下:

但是这个本地线程(pstack产生的)如何与Java线程的堆栈dump(jstack产生的)文件中对应起来呢? 很简单, 在Java 线程的dump文件中, 每个线程都有tid=...nid=...的属性, 通过这些属性可以对应到相应的本地线程,我们先看Java线程的第一行, 里面有一个属性为"nid=",如:

其中nid就是native thread id, 也就是指的本地线程中的LWPID, 二者是相同的, 只不过java线程中的nid中用十六进制来表示, 而本地线程中的id用十进制表示。
例如上面的例子中3368的十六进制表示为0xd28.在Java线程中查找nid=0xd28即是本地线程对应Java线程。 即:

二者是表示是同一个线程。 在本例中, Java线程和本地线程的映射关系(即java线程中nid与本地线程中LWP的值相等的)见下图

再次强调:
Java线程实际上和本地线程指的是同一个东西, 只有本地线程才
是真正的线程实体, Java线程实际上就是指这个本地线程, 它并不是一个另外存在的的实体。
下一篇文章咱们开始读懂锁.....
本着学习交流之目的,如有错误或不当之处,欢迎批评指正,共同进步!
2017年8月16日起,慧测免费公开课系列正式开启——
连续两个月的Java语言+Selenium实战尽在慧测腾讯课堂!
软件测试工程师专属的Java语言学习盛宴!
紧密围绕性能测试、自动化测试、测试开发所必备的Java编程知识讲解!
为后续一个月免费的Selenium实战课程学习做准备!
本系列公开课报名地址:
https://ke.qq.com/course/229575#tuin=3e1d63a
或者扫描下方二维码直接报名

感谢您关注慧测:
慧测官网:www.huicewang.com
慧测公开课服务群: 623636110
课程咨询微信/QQ:18518511087/2657535456
公众号:慧测


