您的位置:首页 > 编程语言 > Java开发

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

2015-10-09 23:10 387 查看
/article/4595064.html

/article/5765542.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)等系统线程处于这种状态。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: