http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html
http://www.cnblogs.com/newcj/archive/2011/07/21/2112760.html
1.分析一个Java线程的转储
为了可以理解/分析线程转储,首先要理解线程转储的各个部分。让我们先拿一个简单的线程堆栈为例,并且去了解他的每个部分。2 线程DUMP的状态分析
"ExecuteThread: '1' " daemon prio=5 tid=0x628330 nid=0xf runnable [0xe4881000..0xe48819e0]
at com.vantive.vanjavi.VanJavi.VanCreateForm(Native Method)
at com.vantive.vanjavi.VanMain.open(VanMain.java:53)
at jsp_servlet._so.__newServiceOrder.printSOSection( __newServiceOrder.java:3547)
at jsp_servlet._so.__newServiceOrder._jspService (__newServiceOrder.java:5652)
at weblogic.servlet.jsp.JspBase.service(JspBase.java:27)
at weblogic.servlet.internal.ServletStubImpl.invokeServlet (ServletStubImpl.java:265)
at weblogic.servlet.internal.ServletStubImpl.invokeServlet (ServletStubImpl.java:200)
at weblogic.servlet.internal.WebAppServletContext.invokeServlet (WebAppServletContext.java:2495)
at weblogic.servlet.internal.ServletRequestImpl.execute (ServletRequestImpl.java:2204)
at weblogic.kernel.ExecuteThread.execute (ExecuteThread.java:139)
at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:120)
In the above Thread Dump, the interesting part to is the first line. The rest of the stuff is nothing more than a general stack trace. Lets analyze the first line here
Execute Thread : 1 说明了线程的名字
daemon 表明这个线程是一个守护线程
prio=5 线程的优先级 (默认是5)
tid Java的线程Id (这个线程在当前虚拟机中的唯一标识).
nid 线程本地标识. 也就是Solaris中的LWP,线程在操作系统中的标识
runnable 线程的状态 (参考上面的)
[x..y] 当前运行的线程在堆中的地址范围,或者是线程堆的起始地址。
这个线程转储的剩余部分是调用堆栈。在这个例子中,这个线程(Execute Thread 1)是操作系统守护线程,当前正在执行一个本地方法vanCreateForm()。
线程DUMP中,线程有三种状态。Runnable ,Wait on condition ,Waiting for monitor entry 和 in Object.wait() 。
2.1 Runnable
该状态表示线程具备所有可运行条件,在运行队列中等待操作系统的调度,或者正在运行。
2.2 Wait on condition
该状态表示线程在等待某个条件的发生。
常见的两种状态:
1)线程在等待网络的读写,比如当网络数据没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。
2)线程在 sleep,等待 sleep的时间到了时候,将被唤醒。
2.3 Waiting for monitor entry 和 in Object.wait()
2.3.1 概述:
对象或者类的锁在某个时刻只能被一个线程拥有,该线程是Active Thread,而其它线程就是Waiting Thread。Waiting Thread线程有如下两种状态:
Waiting for monitor entry:线程没有获取过锁,在等待获取锁。
in Object.wait():线程已获取锁,处于运行状态,但又执行了wait()方法将锁释放掉,并仍然等待该锁。
2.3.2 具体解释:
在多线程的 JAVA程序中,实现线程之间的同步,就要说说 Monitor。 Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。
1)线程如何进入Entry Set
synchronized保护起来的代码段为临界区。
当线程申请进行临界区时,它就进入了Entry Set。此时有两种情况:
A. 该 monitor不被其它线程拥有, Entry Set里面也没有其它等待线程。本线程即成为相应类或者对象的 Monitor的 Owner,执行临界区的代码。
B. 该 monitor被其它线程拥有,本线程在 Entry Set队列中等待。
在第一种情况下,线程将处于 “Runnable”的状态,而第二种情况下,线程 DUMP会显示处于 “waiting for monitor entry”。如下所示:
Html代码
1. "Thread-0" prio=10 tid=0x08222eb0 nid=0x9 waiting for monitor entry [0xf927b000..0xf927bdb8]
2.
3. at testthread.WaitThread.run(WaitThread.java:39)
4.
5. - waiting to lock <0xef63bf08> (a java.lang.Object)
6.
7. - locked <0xef63beb8> (a java.util.ArrayList)
8.
9. at java.lang.Thread.run(Thread.java:595)
2)线程如何进入Wait Set
当线程获得了 Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。只有当别的线程在该对象上调用了 notify() 或者 notifyAll() , “ Wait Set”队列中线程才得到机会去竞争,但是只有一个线程获得对象的 Monitor,恢复到运行态。在 “Wait Set”中的线程, DUMP中表现为: in Object.wait(),类似于:
Html代码
1. "Thread-1" prio=10 tid=0x08223250 nid=0xa in Object.wait() [0xef47a000..0xef47aa38]
2.
3. at java.lang.Object.wait(Native Method)
4.
5. - waiting on <0xef63beb8> (a java.util.ArrayList)
6.
7. at java.lang.Object.wait(Object.java:474)
8.
9. at testthread.MyWaitThread.run(MyWaitThread.java:40)
10.
11. - locked <0xef63beb8> (a java.util.ArrayList)
12.
13. at java.lang.Thread.run(Thread.java:595)
仔细观察上面的 DUMP信息,你会发现它有以下两行:
- locked <0xef63beb8> (a java.util.ArrayList)
- waiting on <0xef63beb8> (a java.util.ArrayList)
这里需要解释一下,为什么先 lock了这个对象,然后又 waiting on同一个对象呢?让我们看看这个线程对应的代码:
Java代码
1. synchronized(obj) {
2. .........
3. obj.wait();
4. .........
5. }
线程的执行中,先用 synchronized 获得了这个对象的 Monitor(对应于 locked <0xef63beb8> )。当执行到 obj.wait(), 线程即放弃了 Monitor的所有权,进入 “wait set”队列(对应于 waiting on <0xef63beb8> )。
往往在你的程序中,会出现多个类似的线程,他们都有相似的 DUMP信息。这也可能是正常的。比如,在程序中,有多个服务线程,设计成从一个队列里面读取请求数据。这个队列就是 lock以及 waiting on的对象。当队列为空的时候,这些线程都会在这个队列上等待,直到队列有了数据,这些线程被 Notify,当然只有一个线程获得了 lock,继续执行,而其它线程继续等待。
3 线程状态为waiting for monitor entry对应的几种线程状态
意味着它 在等待进入一个临界区 ,所以它在”Entry Set“队列中等待。4 线程状态为waiting on condition对应的几种线程状态
此时线程状态一般都是 Blocked:
java.lang.Thread.State: BLOCKED (on object monitor)
说明它在等待另一个条件的发生,来把自己唤醒,或者干脆它是调用了 sleep(N)。5 线程状态为in Object.wait()对应的几种线程状态
此时线程状态大致为以下几种:
java.lang.Thread.State: WAITING (parking):一直等那个条件发生;
java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。
此时线程状态大致为以下几种:
java.lang.Thread.State: TIMED_WAITING (on object monitor);
java.lang.Thread.State: WAITING (on object monitor);
一般都是RMI相关线程(RMI RenewClean、 GC Daemon、RMI Reaper),GC线程(Finalizer),引用对象垃圾回收线程(Reference Handler)等系统线程处于这种状态。