线程的生命周期状态

798 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情

java中线程的生命周期分为6种状态。Thread 类有一个实例属性和一个实例方法专门用于保存和获取线程的状态。其中,用于保存线程 Thread 实例状态的实例属性为:

  private volatile int threadStatus = 0//以整数的形式保存线程的状态。

Thread 类用于获取线程状态的实例方法为:

  public State getState() {
        // get current thread state
        return sun.misc.VM.toThreadState(threadStatus);
    }

State 是一个内部枚举类,定义了 6 个枚举常量,分别代表 Java 线程的 6 种状态,具体如下:

      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;//终止
    }

在 Thread.State 定义的 6 种状态中,有四种是比较常见的状态,它们是:NEW 状态、RUNNABLE状态、TERMINATED 状态、TIMED_WAITING 状态。

1. 常见的几种状态

1.1 NEW 新建状态

Java 源码对 NEW 状态的注释说明是:创建成功但是没有调用 start()方法启动的 Thread 线程实例,都处于 NEW 状态。当然,并不是 Thread 线程实例的 start()方法一经调用,其状态从 NEW 状态到 RUNNABLE
状态,此时并不意味着线程就立即获取 CPU 时间片并且立即执行,中间需要一系列的操作系统内部操作

1.2 RUNNABLE 可执行状态

       一个操作系统线程如果处于就绪状态表示该线程已经满足了执行条件的,但是还不能执行。处于就绪状态的线程,需要等待系统的调度,获取 CPU 时间片,然后上 CPU 执行;一旦就绪状态被系统选中,获得 CPU 时间片,线程就开始占用 CPU,开始执行线程的代码,这时候线程的操作系统状态发生了改变——进入了运行状态。

       操作系统中,处于运行状态的线程在 CPU 时间片用完之后,又回到就绪状态,等待 CPU 的下一次调度。就这样,操作系统线程在就绪状态和执行状态之间,被系统反复的调度,这种情况会一直持续,直到线程的代码逻辑执行完成、或者异常终止。这时线程的操作系统状态又发生了改变,进入了线程的最后状态——TERMINATED 终止状态。

       就绪状态和运行状态,都是操作系统中的线程状态。在 Java 语言中,并没有细分这两种状态,而是将这两种状态合并成同一种状态——RUNNABLE(可执行)状态。因此,在 Thread.State 枚举类中,没有定义线程的就绪状态和运行状态,只是定义了 RUNNABLE 可执行态。这就是 Java线程状态和操作系统中的线程状态有所不同的地方

1.3 TERMINATED 终止状态

       处于RUNNABLE状态的线程,在run()方法执行完成之后,就变成了终止状态TERMINATED了。当然,如果在 run()方法执行过程中发生了运行时异常而没有被捕获,run()方法将被异常终止,线程也会变成 TERMINATED 状态。

1.4 TIMED_WAITING 限时等待状态

       线程处于一种特殊的等待状态,准确的说,线程处于限时等待状态。能让线程处于限时等待状态的操作,大致有以下几种:

Thread.sleep(int n):使得当前线程进入限时等待状态,等待时间为 n 毫秒。
Object.wait():带时限的抢占对象的 monitor 锁。
Thread.join():带时限的线程合并。
LockSupport.parkNanos():让线程等待,时间以纳秒为单位。
LockSupport.parkUntil():让线程等待,时间可以灵活设置。

2.一个线程状态的简单演示案例

public class StatusDemo {

    /**
     * 每个线程执行的轮次
     */
    public static final long MAX_TURN = 5;

    /**
     * 线程编号
     */
    static int threadSeqNumber = 0;

    /**
     * 全局的静态线程列表
     */
    static List<Thread> threadList = new ArrayList<>();

    /**
     * 输出静态线程列表中,每个线程的状态
     */
    private static void printThreadStatus() {
        for (Thread thread : threadList) {
            System.out.println(thread.getName() + "状态为: " + thread.getState());
        }
    }

    /**
     * 向全局的静态线程列表加入线程
     */
    private static void addStatusThread(Thread thread) {
        threadList.add(thread);
    }

    public static void sleepMilliSeconds(int millisecond) {
        LockSupport.parkNanos(millisecond * 1000L * 1000L);
    }

    static class StatusDemoThread extends Thread {
        public StatusDemoThread() {
            super("statusPrintThread" + (++threadSeqNumber));
            //将自己加入到全局的静态线程列表
            addStatusThread(this);
        }

        @Override
        public void run() {
            System.out.println(getName() + ", 状态为:" + getState());
            for (int turn = 0; turn < MAX_TURN; turn++) {
                // 线程睡眠
                sleepMilliSeconds(500);
                // 输出所有线程的状态
                printThreadStatus();
            }
            System.out.println(getName() + "- 运行结束。");
        }
    }

    public static void main(String[] args) throws IOException {
        // 将 main 线程加入全局列表
        addStatusThread(Thread.currentThread());
        // 新建三条线程,这些线程在构造器中会将自己加入全局列表
        Thread sThread1 = new StatusDemoThread();
        System.out.println((sThread1.getName() + "- 状态为" + sThread1.getState()));
        Thread sThread2 = new StatusDemoThread();
        System.out.println((sThread2.getName() + "- 状态为" + sThread1.getState()));
        Thread sThread3 = new StatusDemoThread();
        System.out.println((sThread3.getName() + "- 状态为" + sThread1.getState()));

        //启动第一个线程
        sThread1.start();
        //等待 500ms 启动第二个线程
        sleepMilliSeconds(500);
        sThread2.start();

        //等待 500ms 启动第三个线程
        sleepMilliSeconds(500);
        sThread3.start();

        sleepMilliSeconds(1000);

    }
}

通过以上结果可以看出,当线程新建之后,在没有启动之前其状态为 NEW;调用其 start( )方法启动之后,其状态为 RUNNABLE;当使用 LockSupport.parkNanos(…)方法使得当前线程等待之后,线程的状态变成了 TIMED_WAITING;等待结束之后,其状态又变成了 RUNNABLE 可执行状态;线程执行完成之后,其状态变成 TERMINATED。