天天看点

浅谈Java线程状态

本文章基于JDK8版本

从官方文档看,Java线程有6种状态,分别为NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED,在类Thread中有更详尽的对于线程状态描述,如下代码所示

/**
* A thread state.  A thread can be in one of the following states:
* <ul>
* <li>{@link #NEW}

*     A thread that has not yet started is in this state.
*     </li>
* <li>{@link #RUNNABLE}

*     A thread executing in the Java virtual machine is in this state.
*     </li>
* <li>{@link #BLOCKED}

*     A thread that is blocked waiting for a monitor lock
*     is in this state.
*     </li>
* <li>{@link #WAITING}

*     A thread that is waiting indefinitely for another thread to
*     perform a particular action is in this state.
*     </li>
* <li>{@link #TIMED_WAITING}

*     A thread that is waiting for another thread to perform an action
*     for up to a specified waiting time is in this state.
*     </li>
* <li>{@link #TERMINATED}

*     A thread that has exited is in this state.
*     </li>
* </ul>
*
* <p>
* A thread can be in only one state at a given point in time.
* These states are virtual machine states which do not reflect
* any operating system thread states.
*
* @since   1.5
* @see #getState
*/
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;
}
           

Javadoc详细介绍了调用哪些方法会出现哪种线程状态,下图线程的状态机更加直观反映线程状态是如何转换的

浅谈Java线程状态

可以通过以下的代码显示线程的状态信息

package com.bruce.thread;

public class ThreadDemo implements Runnable {

    public static Thread thread1;
    public static ThreadDemo obj;

    public static void main(String[] args) {
        obj = new ThreadDemo();
        thread1 = new Thread(obj);

        System.out.println("State of thread1 after creating it - " + thread1.getState());
        thread1.start();

        System.out.println("State of thread1 after calling start() method on it - " + thread1.getState());
    }

    @Override
    public void run() {
        ThreadTest myThread = new ThreadTest();
        Thread thread2 = new Thread(myThread);

        System.out.println("State of thread2 after creating it - " + thread2.getState());
        thread2.start();

        System.out.println("State of thread2 after calling start() method on it - " + thread2.getState());

        try {
            Thread.sleep(100);
        } catch (Exception e) {
           e.printStackTrace();
        }
        System.out.println("State of thread2 after calling sleep() method on it - " + thread2.getState());

        try {
            thread2.join();
        } catch (Exception e) {
           e.printStackTrace();
        }
        System.out.println("State of thread2 when it has finished it's execution - " + thread2.getState());
    }
}

class ThreadTest implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
           e.printStackTrace();
        }
        System.out.println("State of thread1 while it call join() method on thread2 - " +
                ThreadDemo.thread1.getState());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
           e.printStackTrace();
        }
    }
}
           

运行结果如下

State of thread1 after creating it - NEW
State of thread1 after calling start() method on it - RUNNABLE
State of thread2 after creating it - NEW
State of thread2 after calling start() method on it - RUNNABLE
State of thread2 after calling sleep() method on it - TIMED_WAITING
State of thread1 while it call join() method on thread2 - WAITING
State of thread2 when it has finished it's execution - TERMINATED
           

在生产环境中,一般不是在代码中调用getState()方法来查看线程状态,而是使用JDK提供的一些工具。

使用jps来查看运行的Java应用线程pid

[[email protected] ~]$ jps -v
25780 RemoteTestRunner -Dfile.encoding=UTF-8
25590 sub.rmi.registry.RegistryImpl 2999 -Dapplication.home=/home1/user/java/jdk.1.6.0_24 -Xms8m
26300 sun.tools.jps.Jps -mlvV -Dapplication.home=/home1/user/java/jdk.1.6.0_24 -Xms8m
           

之后用获得的pid来查看线程堆栈信息

[[email protected] ~]$ jstack 5824
           

那么如何读线程堆栈信息,看下面的线程堆栈信息

第一行信息分别是

线程名(pool-1-thread-13):执行线程名称(如果实现了java.util.concurrent.ThreadFactory接口的类,如Executor,则名称为pool-数字-thread-数字)

优先级(prio=6): 当前线程优先级

线程id(tid=0x000000000729a000):可以通过线程id获取线程的CPU使用率和内存使用率

线程状态(runnable):表示线程的当前状态

线程调用栈

"pool-1-thread-13" prio=6 tid=0x000000000729a000 nid=0x2fb4 runnable [0x0000000007f0f000] java.lang.Thread.State: RUNNABLE
              at java.net.SocketInputStream.socketRead0(Native Method)
              at java.net.SocketInputStream.read(SocketInputStream.java:129)
              at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
              at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
              at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
              - locked <0x0000000780b7e688> (a java.io.InputStreamReader)
              at java.io.InputStreamReader.read(InputStreamReader.java:167)
              at java.io.BufferedReader.fill(BufferedReader.java:136)
              at java.io.BufferedReader.readLine(BufferedReader.java:299)
              - locked <0x0000000780b7e688> (a java.io.InputStreamReader)
              at java.io.BufferedReader.readLine(BufferedReader.java:362)
           

读懂了线程堆栈信息,那么分析线程堵塞,线程等待等一些线程相关问题就可以有据可循,下面介绍几个案例帮助理解线程问题分析。

线程堵塞:

从下面的堆栈信息可以看出线程BLOCKED_TEST pool-1-thread-1占有锁0x0000000780a000b0, 线程BLOCKED_TEST pool-1-thread-2和线程BLOCKED_TEST pool-1-thread-3等待获得锁0x0000000780a000b0,由此可知 BLOCKED_TEST pool-1-thread-1堵塞线程BLOCKED_TEST pool-1-thread-2和线程BLOCKED_TEST pool-1-thread-3

"BLOCKED_TEST pool-1-thread-1" prio=6 tid=0x0000000006904800 nid=0x28f4 runnable [0x000000000785f000]
   java.lang.Thread.State: RUNNABLE
                at java.io.FileOutputStream.writeBytes(Native Method)
                at java.io.FileOutputStream.write(FileOutputStream.java:282)
                at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
                at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
                - locked <0x0000000780a31778> (a java.io.BufferedOutputStream)
                at java.io.PrintStream.write(PrintStream.java:432)
                - locked <0x0000000780a04118> (a java.io.PrintStream)
                at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
                at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
                at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:85)
                - locked <0x0000000780a040c0> (a java.io.OutputStreamWriter)
                at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:168)
                at java.io.PrintStream.newLine(PrintStream.java:496)
                - locked <0x0000000780a04118> (a java.io.PrintStream)
                at java.io.PrintStream.println(PrintStream.java:687)
                - locked <0x0000000780a04118> (a java.io.PrintStream)
                at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:44)
                - locked <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)
                at com.nbp.theplatform.threaddump.ThreadBlockedState$1.run(ThreadBlockedState.java:7)
                at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
                at java.lang.Thread.run(Thread.java:662)

   Locked ownable synchronizers:
                - <0x0000000780a31758> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"BLOCKED_TEST pool-1-thread-2" prio=6 tid=0x0000000007673800 nid=0x260c waiting for monitor entry [0x0000000008abf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:43)
                - waiting to lock <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)
                at com.nbp.theplatform.threaddump.ThreadBlockedState$2.run(ThreadBlockedState.java:26)
                at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
                at java.lang.Thread.run(Thread.java:662)
   Locked ownable synchronizers:
               - <0x0000000780b0c6a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"BLOCKED_TEST pool-1-thread-3" prio=6 tid=0x00000000074f5800 nid=0x1994 waiting for monitor entry [0x0000000008bbf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:42)
                - waiting to lock <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)
                at com.nbp.theplatform.threaddump.ThreadBlockedState$3.run(ThreadBlockedState.java:34)
                at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
                at java.lang.Thread.run(Thread.java:662)
   Locked ownable synchronizers:
                - <0x0000000780b0e1b8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
           

死锁:

如下线程堆栈信息中,可以看到线程DEADLOCK_TEST-1占有锁0x00000007d58f5e48,等待获取锁0x00000007d58f5e60,线程DEADLOCK_TEST-2占有锁0x00000007d58f5e60,等待获取锁0x00000007d58f5e78,线程DEADLOCK_TEST-3占有锁0x00000007d58f5e78,等待获取锁0x00000007d58f5e48。每个线程都在等待获取另一个线程的锁,形成死锁状态

"DEADLOCK_TEST-1" daemon prio=6 tid=0x000000000690f800 nid=0x1820 waiting for monitor entry [0x000000000805f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)
                - waiting to lock <0x00000007d58f5e60> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)
                - locked <0x00000007d58f5e48> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)
   Locked ownable synchronizers:
                - None

"DEADLOCK_TEST-2" daemon prio=6 tid=0x0000000006858800 nid=0x17b8 waiting for monitor entry [0x000000000815f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)
                - waiting to lock <0x00000007d58f5e78> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)
                - locked <0x00000007d58f5e60> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)
   Locked ownable synchronizers:
               - None
"DEADLOCK_TEST-3" daemon prio=6 tid=0x0000000006859000 nid=0x25dc waiting for monitor entry [0x000000000825f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)
                - waiting to lock <0x00000007d58f5e48> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)
                - locked <0x00000007d58f5e78> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)
   Locked ownable synchronizers:
                - None
           

持续等待从远程服务器获取信息

虽然线程堆栈信息显示是RUNNABLE状态,但是如果长时间观察下能发现一直在等待获取数据状态,可以认为此时线程已经处于堵塞状态(比如没有设置timeout,导致线程一直等待回复)

"socketReadThread" prio=6 tid=0x0000000006a0d800 nid=0x1b40 runnable [0x00000000089ef000]
   java.lang.Thread.State: RUNNABLE
                at java.net.SocketInputStream.socketRead0(Native Method)
                at java.net.SocketInputStream.read(SocketInputStream.java:129)
                at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
                at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
                at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
                - locked <0x00000007d78a2230> (a java.io.InputStreamReader)
                at sun.nio.cs.StreamDecoder.read0(StreamDecoder.java:107)
                - locked <0x00000007d78a2230> (a java.io.InputStreamReader)
                at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:93)
                at java.io.InputStreamReader.read(InputStreamReader.java:151)
                at com.nbp.theplatform.threaddump.ThreadSocketReadState$1.run(ThreadSocketReadState.java:27)
                at java.lang.Thread.run(Thread.java:662)
           

等待:

从线程堆栈信息可以看出线程IoWaitThread等待从LinkedBlockingQueue中获取新消息,如果LinkedBlockingQueue没有数据,那么线程将一直等待下去

"IoWaitThread" prio=6 tid=0x0000000007334800 nid=0x2b3c waiting on condition [0x000000000893f000]
   java.lang.Thread.State: WAITING (parking)
                at sun.misc.Unsafe.park(Native Method)
                - parking to wait for  <0x00000007d5c45850> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
                at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
                at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)
                at java.util.concurrent.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:440)
                at java.util.concurrent.LinkedBlockingDeque.take(LinkedBlockingDeque.java:629)
                at com.nbp.theplatform.threaddump.ThreadIoWaitState$IoWaitHandler2.run(ThreadIoWaitState.java:89)
                at java.lang.Thread.run(Thread.java:662)
           

希望通过以上的案例可以帮助到小伙伴加深对线程的认识。

参考资料:

1.How to Analyze Java Thread Dumps

2.lifecycle-and-states-of-a-thread-in-java

3.How to Analyze Java Thread Dumps