java线程学习(七)—java线程转储与堆栈

来源:互联网 发布:电脑游戏录像软件 编辑:程序博客网 时间:2024/05/17 05:19

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线程的转储
为了可以理解/分析线程转储,首先要理解线程转储的各个部分。让我们先拿一个简单的线程堆栈为例,并且去了解他的每个部分。
"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()。
2 线程DUMP的状态分析
     线程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“队列中等待。
此时线程状态一般都是 Blocked:
java.lang.Thread.State: BLOCKED (on object monitor)
4 线程状态为waiting on condition对应的几种线程状态
说明它在等待另一个条件的发生,来把自己唤醒,或者干脆它是调用了 sleep(N)。
此时线程状态大致为以下几种:
java.lang.Thread.State: WAITING (parking):一直等那个条件发生;
java.lang.Thread.State: TIMED_WAITING (parking或sleeping):定时的,那个条件不到来,也将定时唤醒自己。
5 线程状态为in Object.wait()对应的几种线程状态
此时线程状态大致为以下几种:
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)等系统线程处于这种状态。

0 0
原创粉丝点击