线程的状态有哪些?它们之间是如何转换的?

参考回答

Java 中线程的状态定义在 Thread.State 枚举中,共有以下几种状态:

  1. NEW(新建状态):线程对象被创建后,还未调用 start() 方法。
  2. RUNNABLE(就绪/运行状态):线程被调用 start() 方法后,可能正在运行,也可能等待 CPU 调度。
  3. BLOCKED(阻塞状态):线程等待获取一个锁时进入此状态。
  4. WAITING(无限期等待状态):线程等待其他线程显式唤醒时进入此状态(如 Object.wait()Thread.join())。
  5. TIMED_WAITING(限时等待状态):线程在等待时有时间限制(如 Thread.sleep()Object.wait(timeout))。
  6. TERMINATED(终止状态):线程执行完毕或因异常退出,进入此状态。

线程的状态并非线性转换,而是通过特定事件在状态之间切换。


详细讲解与拓展

1. 线程的状态与转换图

NEW → RUNNABLE ↔ BLOCKED
         ↔ WAITING ↔ TIMED_WAITING
RUNNABLE → TERMINATED

2. 各状态详解与转换

1. NEW(新建状态)
  • 进入条件:调用 new Thread() 创建线程对象。
  • 退出条件:调用线程的 start() 方法。
  • 注意:处于 NEW 状态的线程未分配系统资源,无法运行。

示例:

Thread thread = new Thread(() -> System.out.println("Running")); // 线程处于 NEW 状态

2. RUNNABLE(就绪/运行状态)
  • 进入条件:调用 start() 后,线程进入就绪队列,等待 CPU 调度。

  • 特点:线程可能正在运行,也可能等待 CPU 分配时间片。

  • 退出条件:

    • 等待资源:进入 BLOCKED。
    • 等待通知:进入 WAITING 或 TIMED_WAITING。
    • 执行结束:进入 TERMINATED。

示例:

thread.start(); // 调用 start 后,线程进入 RUNNABLE 状态

3. BLOCKED(阻塞状态)
  • 进入条件:线程试图进入同步代码块,但锁已被其他线程持有。
  • 退出条件:获取到锁后进入 RUNNABLE。

示例:

synchronized (lock) {
    // 当前线程获取到锁
}

如果其他线程尝试获取 lock,但锁已被占用,它们会进入 BLOCKED 状态。


4. WAITING(无限期等待状态)
  • 进入条件:线程调用以下方法进入 WAITING:
    • Object.wait()(无超时时间)。
    • Thread.join()(无超时时间)。
    • LockSupport.park()
  • 退出条件:
    • 被其他线程显式唤醒(如 notify()unpark())。
  • InterruptedException 被抛出。

示例:

synchronized (lock) {
    lock.wait(); // 线程进入 WAITING 状态
}

5. TIMED_WAITING(限时等待状态)
  • 进入条件:线程调用以下方法,带超时时间:
    • Thread.sleep(time)
    • Object.wait(time)
    • Thread.join(time)
    • LockSupport.parkNanos(time)parkUntil(time)
  • 退出条件:
    • 时间到期。
  • 被显式唤醒(如 notify()unpark())。

示例:

Thread.sleep(1000); // 当前线程进入 TIMED_WAITING 状态

6. TERMINATED(终止状态)
  • 进入条件:线程执行完 run() 方法,或者线程抛出未捕获异常。
  • 特点:线程终止后不可再次启动,否则会抛出 IllegalThreadStateException
  • 注意:终止状态的线程已经释放所有资源。

示例:

thread.join(); // 等待线程进入 TERMINATED 状态

3. 示例代码:观察线程状态

public class ThreadStateExample {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000); // TIMED_WAITING
                synchronized (ThreadStateExample.class) {
                    ThreadStateExample.class.wait(); // WAITING
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.out.println("State: " + thread.getState()); // NEW
        thread.start();
        System.out.println("State: " + thread.getState()); // RUNNABLE
        Thread.sleep(500);
        System.out.println("State: " + thread.getState()); // TIMED_WAITING
        synchronized (ThreadStateExample.class) {
            ThreadStateExample.class.notify();
        }
        thread.join();
        System.out.println("State: " + thread.getState()); // TERMINATED
    }
}

4. 状态转换的注意事项

  1. RUNNABLE 不等于运行中:线程处于 RUNNABLE 状态时,可能等待 CPU 调度,未必实际在执行。

  2. BLOCKED 与 WAITING 的区别:

  • BLOCKED:等待进入同步块,未获取锁。
  • WAITING:线程等待显式唤醒。
  1. 不可重复启动:线程从 TERMINATED 状态不可再次启动。

5. 总结

  • Java 的线程有 6 种状态,主要通过方法调用和线程调度切换。
  • 理解线程状态有助于调试并发程序,尤其是定位死锁或阻塞问题。
  • 可以通过 Thread.getState() 方法监控线程状态,结合同步工具(如锁和 wait/notify)合理设计多线程程序。

发表评论

后才能评论