在上一篇文章《Java多线程(一):多线程的三种创建方式》中,我们学习了如何通过继承 Thread 类、实现 Runnable 接口以及使用 CallableFutureTask 三种方式来创建线程。创建线程只是第一步,理解线程在运行过程中的生命周期状态转换,是掌握多线程编程的关键。

本文将深入探讨 Java 线程的六种状态、它们之间的转换关系,以及影响状态变化的核心方法,帮助你构建对线程运行机制的完整认知。


一、Java线程的六种状态

在 Java 中,线程的生命周期由 java.lang.Thread.State 枚举类定义,共包含六种状态:

状态说明
NEW新建状态。线程对象已创建,但尚未调用 start() 方法。
RUNNABLE可运行状态。线程已启动,正在 JVM 中执行,或等待操作系统调度 CPU 时间片。
BLOCKED阻塞状态。线程等待获取一个监视器锁(monitor lock),以进入同步代码块或方法。
WAITING无限等待状态。线程主动等待,直到被其他线程显式唤醒(如 notify()notifyAll())。
TIMED_WAITING限时等待状态。线程在指定时间内等待,时间到后自动恢复。
TERMINATED终止状态。线程执行完毕或因异常退出。

💡 注意:RUNNABLE 状态实际上包含了操作系统层面的“就绪”和“运行”两种状态,Java 线程模型将其统一视为可运行。


二、线程状态转换图

下图展示了线程在不同操作下的状态转换关系:

                            +-----------------+
                            |      NEW        |
                            +--------+--------+
                                     |
                                     | start()
                                     v
                            +-----------------+
                            |    RUNNABLE     |
                            +--------+--------+
                                     |
           +-------------------------+-------------------------+
           |                         |                         |
           | wait()                  | sleep(), join(),        | synchronized
           | wait(timeout)           | wait(timeout)           | block
           v                         v                         v
    +--------------+       +---------------------+    +--------------+
    |   WAITING    |       |   TIMED_WAITING     |    |   BLOCKED    |
    +------+-------+       +----------+----------+    +------+-------+
           |                         |                        |
           | notify()/notifyAll()    | timeout / interrupt()  | acquire lock
           +-------------------------+------------------------+
                                     |
                                     |
                                     v
                            +-----------------+
                            |   TERMINATED    |
                            +-----------------+

下面我们通过代码示例,逐一解析每种状态的触发条件和转换过程。


三、状态详解与代码示例

1. NEW → RUNNABLE

当线程对象创建后,调用 start() 方法,线程进入 RUNNABLE 状态。

Thread thread = new Thread(() -> {
    System.out.println("线程正在运行");
});
System.out.println("创建后状态: " + thread.getState()); // NEW

thread.start();
System.out.println("启动后状态: " + thread.getState()); // 很可能是 RUNNABLE

⚠️ 注意:start() 调用后,线程不一定立即执行,因此状态可能是 RUNNABLE


2. RUNNABLE → BLOCKED

当多个线程竞争同一个同步锁时,未获得锁的线程会进入 BLOCKED 状态。

Object lock = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lock) {
        try {
            Thread.sleep(5000); // 持有锁5秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

Thread t2 = new Thread(() -> {
    synchronized (lock) {
        System.out.println("t2 获取到锁");
    }
});

t1.start();
t2.start();

// 观察 t2 的状态
new Thread(() -> {
    while (t2.isAlive()) {
        System.out.println("t2 当前状态: " + t2.getState()); // BLOCKED
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

输出中会看到 t2 在等待锁时处于 BLOCKED 状态。


3. RUNNABLE → WAITING

线程调用 wait()join()(无参)、LockSupport.park() 时,会进入无限等待状态,必须由其他线程调用 notify()notifyAll() 才能唤醒。

Thread waiter = new Thread(() -> {
    synchronized (lock) {
        try {
            System.out.println("进入等待...");
            lock.wait(); // 进入 WAITING 状态
            System.out.println("被唤醒!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

Thread notifier = new Thread(() -> {
    synchronized (lock) {
        System.out.println("准备唤醒...");
        lock.notify();
    }
});

waiter.start();
Thread.sleep(1000);
notifier.start();

使用 jstack 或监控线程状态可观察到 waiter 线程处于 WAITING 状态。


4. RUNNABLE → TIMED_WAITING

调用以下方法时,线程进入限时等待:

  • Thread.sleep(long millis)
  • wait(long timeout)
  • join(long millis)
  • LockSupport.parkNanos()
Thread sleeper = new Thread(() -> {
    try {
        Thread.sleep(3000); // 进入 TIMED_WAITING
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

sleeper.start();
System.out.println("睡眠中状态: " + sleeper.getState()); // TIMED_WAITING

3秒后自动恢复为 RUNNABLE


5. 任意状态 → TERMINATED

线程执行完 run() 方法或因未捕获异常退出时,进入终止状态。

Thread t = new Thread(() -> {
    System.out.println("线程开始");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        return;
    }
    System.out.println("线程结束");
});

t.start();
t.join(); // 等待线程结束
System.out.println("最终状态: " + t.getState()); // TERMINATED

四、如何监控线程状态?

你可以通过 Thread.getState() 方法获取线程当前状态,这在调试多线程问题时非常有用。

Thread t = new Thread(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE
t.sleep(100);
System.out.println(t.getState()); // TIMED_WAITING
// t.join() 会阻塞当前线程(main线程),直到线程t执行完毕
t.join();
System.out.println(t.getState()); // TERMINATED

五、常见误区

  1. run() vs start()
    直接调用 run() 方法不会启动新线程,也不会改变线程状态,它只是普通方法调用,线程状态仍为 NEW

  2. BLOCKED vs WAITING

    • BLOCKED 是被动等待锁。
    • WAITING 是主动调用 wait() 进入等待,需 notify() 唤醒。
  3. 状态不是实时的
    调用 getState() 是瞬时快照,线程状态可能在两次调用之间已发生变化。


六、总结

状态触发条件变化条件
NEW线程对象创建调用 start()
RUNNABLE正在运行或就绪——
BLOCKED等待进入 synchronized 块获取到锁
WAITINGwait()join()park()notify()/notifyAll()
TIMED_WAITINGsleep()wait(timeout)超时或中断
TERMINATED线程执行结束不可恢复

理解线程的生命周期,是排查死锁、活锁、性能瓶颈等问题的基础。在后续文章中,我们将深入探讨线程同步机制(如 synchronizedReentrantLock)和线程间通信,这些机制正是通过控制线程状态转换来实现的。