线程有几种状态,状态之间是怎样流转的?

面试考察点

当面试官询问线程状态时,其核心考察点远不止于记忆几个状态名称。他们通常希望了解:

  1. 对 Java 并发模型基础概念的掌握程度:这是并发编程的基石。
  2. 对线程生命周期和状态转换触发条件的深入理解:面试官不仅仅想知道 “有哪几种状态”,更想知道 “在什么情况下,线程会从A状态变成B状态”。
  3. 将理论联系实际调试和开发的能力:是否能在程序卡死、性能低下时,通过线程状态(如 jstack 命令输出)快速定位问题(如死锁、线程饥饿、无限等待)。
  4. 对底层机制(JVM 与操作系统调度)的认知:理解 RUNNABLE 状态与操作系统线程状态的映射关系。

核心答案

在 Java 中,线程的状态在 java.lang.Thread.State 枚举中明确定义,共有 6 种:

  1. NEW:新建状态,线程被创建但尚未启动。
  2. RUNNABLE:可运行状态,包括了操作系统线程状态中的 就绪(Ready) 和 运行中(Running)。线程可能在等待 CPU 时间片,也可能正在执行。
  3. BLOCKED:阻塞状态,线程在等待进入一个由 synchronized 关键字保护的同步块或方法时陷入的状态,其前提是所需的锁正被其他线程持有。
  4. WAITING:无限期等待状态,线程进入此状态后,需要等待其他线程显式地唤醒。例如,调用了 Object.wait()Thread.join() 或 LockSupport.park() 方法。
  5. TIMED_WAITING:超时等待状态,与 WAITING 类似,但设置了最长等待时间。例如,调用了 Thread.sleep(long millis)Object.wait(long timeout)Thread.join(long millis) 等方法。
  6. TERMINATED:终止状态,表示线程已经执行完毕。

状态的流转是一个闭环:线程由 NEW 开始,通过 start() 进入 RUNNABLE。在 RUNNABLE 中,可能因等待锁、主动放弃 CPU 或等待条件而进入 BLOCKEDWAITING 或 TIMED_WAITING。当等待的条件满足或被中断后,线程会重新回到 RUNNABLE 状态等待调度。最终,线程执行完毕或发生未捕获异常而进入 TERMINATED 状态。

深度解析

原理与机制

  • RUNNABLE 的广义性:这是 Java 语言层面的状态,它对应了操作系统线程状态中的 “就绪” 和 “运行”。因此,一个处于 RUNNABLE 状态的 Java 线程,在操作系统看来可能正在运行,也可能在就绪队列中等待 CPU。这是理解 Java 线程调度的关键。
  • BLOCKED 与 WAITING 的本质区别:这是最常见的混淆点。
    • BLOCKED 是 “主动的、竞争的”。线程在积极尝试获取一个已经锁定的 synchronized 监视器锁。一旦锁被释放,所有在该锁上 BLOCKED 的线程会去竞争,只有一个能成功。
    • WAITING/TIMED_WAITING 是 “被动的、协作的”。线程通常是主动释放了锁(如调用 wait() 会释放锁),然后进入等待队列,直到被其他线程通过 notify()/notifyAll() 或条件满足(超时、中断)唤醒。被唤醒后,线程需要重新去获取锁(这可能会再次进入 BLOCKED 状态),才能恢复到 RUNNABLE
  • 流转触发点:每个状态变化都由特定的方法调用或系统事件触发,理解这些 “扳机” 是分析并发问题的核心。

代码示例与状态流转:

public class ThreadStateDemo {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();

        Thread thread = new Thread(() -> {
            synchronized (lock) {
                try {
                    // 从 RUNNABLE 进入 TIMED_WAITING
                    Thread.sleep(1000);
                    // 从 RUNNABLE 进入 WAITING,并释放 lock
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 状态 1: NEW
        System.out.println("1. After creation: " + thread.getState());

        thread.start();
        Thread.sleep(10); // 确保主线程稍后执行,让子线程有机会运行
        // 状态 2: RUNNABLE (子线程已启动,可能在执行或等待CPU)
        System.out.println("2. After start: " + thread.getState());

        Thread.sleep(100);
        // 状态 3: TIMED_WAITING (因为 sleep)
        System.out.println("3. During sleep: " + thread.getState());

        Thread.sleep(1000); // 等待 sleep 结束
        // 状态 4: WAITING (因为 wait)
        System.out.println("4. During wait: " + thread.getState());

        synchronized (lock) {
            lock.notify(); // 唤醒子线程
            Thread.sleep(100);
            // 状态 5: BLOCKED (子线程被唤醒,但需要重新获取 lock,而 lock 被主线程持有)
            System.out.println("5. After notify, trying to re-acquire lock: " + thread.getState());
        }

        Thread.sleep(100);
        // 状态 6: TERMINATED (子线程执行完毕)
        System.out.println("6. After all: " + thread.getState());
    }
}

常见误区与最佳实践

  • 误区1:RUNNABLE 就等于“正在运行”。不对,它只代表 Java 线程 “有资格运行”,可能正在运行,也可能在等待系统资源。
  • 误区2:线程休眠(sleep)时持有锁。是的,sleep 不会释放任何监视器锁(如 synchronized 或 Lock 对象),这与 wait() 不同。
  • 最佳实践:在生产环境排查性能问题时,应熟练使用 jstackjconsole 或可视化工具(如 Arthas)来捕获线程转储(Thread Dump),并准确识别出 BLOCKEDWAITING 状态的线程及其堆栈,这能快速定位锁竞争、死锁或资源等待问题。
posted @ 2026-03-10 14:34  JAVA笔录  阅读(7)  评论(0)    收藏  举报