学习java线程状态和看懂thread dump文件中的线程信息

线程的状态是一个很重要的东西,因此thread dump中会显示这些状态,通过对这些状态的分析,能够得出线程的运行状况,进而发现可能存在的问题。线程的状态在Thread.State这个枚举类型中定义:

 public enum State 
 {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,
        
        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,
        
        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or 
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,
    
        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the 
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         * 
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.  
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call 
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on 
         * that object. A thread that has called <tt>Thread.join()</tt> 
         * is waiting for a specified thread to terminate.
         */
        WAITING,
        
        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of 
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> 
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
}

下面简单地介绍下这几个状态值:

1、NEW:

     我们知道每一个线程,在堆内存中都有一个对应的Thread对象。Thread t = new Thread();当刚刚在堆内存中创建Thread对象,还没有调用t.start()方法之前,线程就处在NEW状态。在这个状态上,线程与普通的java对象没有什么区别,就仅仅是一个堆内存中的对象。

2、RUNNABLE

     该状态表示线程具备所有运行条件,在运行队列中准备操作系统的调度,或者正在运行。 这个状态的线程比较正常,但如果线程长时间停留在在这个状态就不正常了,这说明线程运行的时间很长(存在性能问题),或者是线程一直得不得执行的机会(存在线程饥饿的问题)。

3、BLOCKED

     线程正在等待获取java对象的监视器(也叫内置锁),即线程正在等待进入由synchronized保护的方法或者代码块。synchronized用来保证原子性,任意时刻最多只能由一个线程进入该临界区域,其他线程只能排队等待。

4、WAITING

     处在该线程的状态,正在等待某个事件的发生,只有特定的条件满足,才能获得执行机会。而产生这个特定的事件,通常都是另一个线程。也就是说,如果不发生特定的事件,那么处在该状态的线程一直等待,不能获取执行的机会。比如说,A线程调用了obj对象的obj.wait()方法,如果没有线程调用obj.notify或obj.notifyAll,那么A线程就没有办法恢复运行;如果A线程调用了LockSupport.park(),没有别的线程调用LockSupport.unpark(A),那么A没有办法恢复运行。

5、TIMED_WAITING

     我们知道J.U.C中很多与线程相关类,都提供了限时版本和不限时版本的API。TIMED_WAITING意味着线程调用了限时版本的API,正在等待时间流逝;当等待时间过去后,线程一样可以恢复运行。如果线程进入了WAITING状态,一定要特定的事件发生才能恢复运行;而处在TIMED_WAITING的线程,如果特定的事件发生或者是时间流逝完毕,都会恢复运行。

6、TERMINATED

     线程执行完毕,执行完run方法正常返回,或者抛出了运行时异常而结束,线程都会停留在这个状态。这个时候线程只剩下Thread对象了,没有什么用了。



接下来我们利用JDK提供的jstack工具,查看下thread dump文件中线程的状态。NEW、RUNNABLE、TERMINATED这3个状态很容易理解,主要讲下其它3种状态。

1、显示BLOCKED状态

package jstack;


public class BlockedState
{
	private static Object object = new Object();

	public static void main(String[] args)
	{
		Runnable task = new Runnable() {

			@Override
			public void run()
			{
				synchronized (object)
				{
					long begin = System.currentTimeMillis();

					long end = System.currentTimeMillis();

					// 让线程运行5分钟,会一直持有object的监视器
					while ((end - begin) <= 5 * 60 * 1000)
					{

					}
				}
			}
		};

		new Thread(task, "t1").start();
		new Thread(task, "t2").start();
	}
}
先获取object的线程会执行5分钟,这5分钟内会一直持有object的监视器,另一个线程无法执行处在BLOCKED状态

C:\Documents and Settings\Administrator>jps
5400 Jps
3364
4752 BlockedState

C:\Documents and Settings\Administrator>jstack 4752
2014-09-23 19:42:28
Full thread dump Java HotSpot(TM) Server VM (20.12-b01 mixed mode):

"DestroyJavaVM" prio=6 tid=0x00856c00 nid=0x1314 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"t2" prio=6 tid=0x27d7a800 nid=0x1350 waiting for monitor entry [0x2833f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at jstack.BlockedState$1.run(BlockedState.java:17)
        - waiting to lock <0x1cfcdc00> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:662)

"t1" prio=6 tid=0x27d79400 nid=0x1338 runnable [0x282ef000]
   java.lang.Thread.State: RUNNABLE
        at jstack.BlockedState$1.run(BlockedState.java:22)
        - locked <0x1cfcdc00> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:662)
通过thread dump可以看到:t2线程确实处在BLOCKED (on object monitor)。waiting for monitor entry 等待进入synchronized保护的区域。


2、显示WAITING状态

package jstack;


public class WaitingState
{
	private static Object object = new Object();

	public static void main(String[] args)
	{
		Runnable task = new Runnable() {

			@Override
			public void run()
			{
				synchronized (object)
				{
					long begin = System.currentTimeMillis();

					long end = System.currentTimeMillis();

					// 让线程运行5分钟,会一直持有object的监视器
					while ((end - begin) <= 5 * 60 * 1000)
					{
						try
						{
							// 进入等待的同时,会进入释放监视器
							object.wait();
						} catch (InterruptedException e)
						{
							e.printStackTrace();
						}
					}
				}
			}
		};

		new Thread(task, "t1").start();
		new Thread(task, "t2").start();
	}
}
C:\Documents and Settings\Administrator>jps
3356 Jps
3364
5784 WaitingState

C:\Documents and Settings\Administrator>jstack 5784
2014-09-23 19:48:43
Full thread dump Java HotSpot(TM) Server VM (20.12-b01 mixed mode):

"DestroyJavaVM" prio=6 tid=0x00856c00 nid=0x1734 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"t2" prio=6 tid=0x27d7e000 nid=0x17f4 in Object.wait() [0x2833f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x1cfcdc00> (a java.lang.Object)
        at java.lang.Object.wait(Object.java:485)
        at jstack.WaitingState$1.run(WaitingState.java:26)
        - locked <0x1cfcdc00> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:662)

"t1" prio=6 tid=0x27d7d400 nid=0x17f0 in Object.wait() [0x282ef000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x1cfcdc00> (a java.lang.Object)
        at java.lang.Object.wait(Object.java:485)
        at jstack.WaitingState$1.run(WaitingState.java:26)
        - locked <0x1cfcdc00> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:662)
可以发现t1和t2都处在WAITING (on object monitor),进入等待状态的原因是调用了in Object.wait()。通过J.U.C包下的锁和条件队列,也是这个效果,大家可以自己实践下。

3、显示TIMED_WAITING状态

package jstack;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TimedWaitingState
{
	// java的显示锁,类似java对象内置的监视器
	private static Lock lock = new ReentrantLock();

	// 锁关联的条件队列(类似于object.wait)
	private static Condition condition = lock.newCondition();

	public static void main(String[] args)
	{
		Runnable task = new Runnable() {

			@Override
			public void run()
			{
				// 加锁,进入临界区
				lock.lock();

				try
				{
					condition.await(5, TimeUnit.MINUTES);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}

				// 解锁,退出临界区
				lock.unlock();
			}
		};

		new Thread(task, "t1").start();
		new Thread(task, "t2").start();
	}
}
C:\Documents and Settings\Administrator>jps
5952 Jps
3364
5732 TimedWaitingState

C:\Documents and Settings\Administrator>jstack 5732
2014-09-23 19:59:44
Full thread dump Java HotSpot(TM) Server VM (20.12-b01 mixed mode):

"DestroyJavaVM" prio=6 tid=0x00856c00 nid=0x169c waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"t2" prio=6 tid=0x27d7d800 nid=0xc30 waiting on condition [0x2833f000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x1cfce5b8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2116)
        at jstack.TimedWaitingState$1.run(TimedWaitingState.java:28)
        at java.lang.Thread.run(Thread.java:662)

"t1" prio=6 tid=0x280d0c00 nid=0x16e0 waiting on condition [0x282ef000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x1cfce5b8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2116)
        at jstack.TimedWaitingState$1.run(TimedWaitingState.java:28)
        at java.lang.Thread.run(Thread.java:662)
可以看到t1和t2线程都处在java.lang.Thread.State: TIMED_WAITING (parking),这个parking代表是调用的JUC下的工具类,而不是java默认的监视器。内置锁和显示锁的差别,可以看我下java并发相关的文章  java并发编程专题  http://blog.csdn.net/aitangyong/article/category/2298385



接下来看下thread dump打印出的线程信息代表什么含义,每个线程的基本都是一样的,我们随便选取一个,分析下:

"t1" prio=6 tid=0x27d7d400 nid=0x17f0 in Object.wait() [0x282ef000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x1cfcdc00> (a java.lang.Object)
        at java.lang.Object.wait(Object.java:485)
        at jstack.WaitingState$1.run(WaitingState.java:26)
        - locked <0x1cfcdc00> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:662)
t1 : 这个是线程名称,可以在new Thread()对象的时候指定,或者调用Thread.setName。有了这个名称,搜索thread dump的时候更加方便。这也就是为什么,一般创建线程的时候需要指定线程名称,而不是使用默认的名称。

prio=6 : 这个代表的是线程的优先级priority,也可以通过Thread类中的API修改。实际应用中,一般不会设置该属性,没有太大的用处。

tid : Java的线程Id (这个线程在当前虚拟机中的唯一标识)。

nid : 线程本地标识,是线程在操作系统中的标识。

[0x282ef000] : 对象的内存地址,通过JVM内存查看工具,能够看出线程是在哪儿个对象上等待。

java.lang.Thread.State: WAITING (on object monitor) : 显示线程当前时刻的状态,括号里面是处于该状态的原因。

最下面以at开头的是线程的调用堆栈,通过它可以看出线程执行代码的流程。


最后我们看下dump文件中Waiting for monitor entry 和 in Object.wait()的区别。首先我们需要注意一点:这2个东西都是针对Object对象的监视器而言的,只要监视器才会有这种区分,因为监视器是JVM内置支持的,所以可以想象的到,JVM内部会特殊处理这个监视器。而JDK5.0之后提供显示锁Lock不不会区分这2种状态,因为Lock对于JVM而言,就是一个普通的java对象。下面这个图,描述了线程和 Monitor之间关系,以及线程的状态转换图: 


从图中可以看出,每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。 简单地理解:“Entry Set”中存放的是等待进入synchronized的线程,“Wait Set”中存放的是已经进入了synchronized但是调用了object.wait的线程。




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。