线程状态(Java)

线程是操作系统调度的基本单位,其生命周期包含一系列状态转换。Java 中线程状态在 java.lang.Thread.State 枚举中明确定义(共 6 种核心状态),由 JVM 或线程操作自动触发切换,不可手动直接修改。本文结合原理、代码和场景,详细拆解每种状态。

一、核心概念:6 种线程状态定义

状态名称 核心含义 触发条件(进入方式) 退出条件(切换方式)
NEW(新建) 线程对象已创建,但未调用 start() 方法(未启动,JVM 未分配执行资源)。 new Thread(...) 创建线程对象。 调用 thread.start() 方法,进入 RUNNABLE
RUNNABLE(可运行) 线程已启动,要么正在 CPU 上执行,要么等待 CPU 调度(就绪态)。 1. NEW 状态调用 start();2. 阻塞 / 等待状态恢复(如锁释放、超时、被唤醒)。 1. 执行完毕(run() 结束)→ TERMINATED;2. 竞争 synchronized 锁失败 → BLOCKED;3. 调用 sleep()/wait() 等 → TIMED_WAITING/WAITING
BLOCKED(阻塞) 线程因竞争 synchronized 锁 失败,被动等待锁释放(仅针对 synchronized)。 线程尝试进入 synchronized 代码块 / 方法,但锁被其他线程持有。 持有锁的线程释放锁,当前线程竞争到锁 → RUNNABLE
WAITING(无限等待) 线程主动放弃执行权,无超时时间,必须被其他线程唤醒才能恢复。 1. Object.wait()(无参,需在 synchronized 中);2. Thread.join()(无参);3. LockSupport.park() 1. 其他线程调用 Object.notify()/notifyAll();2. join() 目标线程执行完毕;3. LockSupport.unpark(thread)
TIMED_WAITING(计时等待) 线程主动放弃执行权,有明确超时时间,超时后自动唤醒(也可手动唤醒)。 1. Thread.sleep(long ms);2. Object.wait(long ms);3. Thread.join(long ms);4. LockSupport.parkNanos() 1. 超时自动唤醒 → RUNNABLE;2. 其他线程提前唤醒(notify()/unpark());3. join() 目标线程提前结束。
TERMINATED(终止) 线程执行完毕(正常退出)或异常终止(未捕获异常)。 1. run() 方法执行完成;2. 线程抛出未捕获的 Exception/Error 状态不可逆,终止后无法再次启动(调用 start() 抛异常)。

关键澄清

  • RUNNABLE 不区分 “正在执行” 和 “等待 CPU 调度”:JVM 层面统一归为 RUNNABLE,操作系统层面的 “就绪态” 和 “运行态” 被 Java 合并;
  • BLOCKED 仅针对 synchronized 锁:ReentrantLock 等 Lock 框架的等待状态属于 WAITING/TIMED_WAITING(通过 Condition.await() 实现);
  • 线程状态是 “被动切换”:开发者无法通过 setState() 手动修改(该方法为 protected,仅 JVM 可调用)。

二、线程状态切换流程图

[NEW] 
    ↓ (调用 start())
[RUNNABLE] 
    ↓ (竞争 synchronized 锁失败)
[BLOCKED] 
    ↓ (获取锁成功)
[RUNNABLE] 
    ↓ (调用 sleep()/wait(long)/join(long))
[TIMED_WAITING] 
    ↓ (超时/被唤醒)
[RUNNABLE] 
    ↓ (调用 wait()/join()/park())
[WAITING] 
    ↓ (被 notify()/unpark()/join 目标结束)
[RUNNABLE] 
    ↓ (run() 结束/抛异常)
[TERMINATED] (不可逆)

三、代码实现:逐个验证线程状态

环境准备

  • JDK 8+(Thread.State 从 JDK 1.5 开始支持);
  • 开发工具:IDEA/Eclipse(通过 thread.getState() 获取状态)。

1. 验证:NEW → RUNNABLE → TERMINATED(基础生命周期)

代码逻辑

创建线程对象(未启动)→ 调用 start() 启动 → 执行完毕后终止。

public class ThreadStateBasic {
    public static void main(String[] args) throws InterruptedException {
        // 1. 创建线程对象(未启动)→ NEW 状态
        Thread thread = new Thread(() -> {
            System.out.println("子线程执行中...");
            // 简单执行逻辑,快速结束
        }, "BasicThread");

        // 打印初始状态:NEW
        System.out.println("1. 线程创建后(未 start):" + thread.getState());

        // 2. 启动线程 → RUNNABLE 状态
        thread.start();
        // 休眠 50ms,让线程有机会进入执行状态(避免主线程先打印)
        Thread.sleep(50);
        System.out.println("2. 线程启动后(执行中):" + thread.getState());

        // 3. 主线程等待子线程执行完毕(join() 阻塞主线程)
        thread.join();
        // 打印终止状态:TERMINATED
        System.out.println("3. 线程执行完毕后:" + thread.getState());
    }
}
运行结果
1. 线程创建后(未 start):NEW
子线程执行中...
2. 线程启动后(执行中):RUNNABLE
3. 线程执行完毕后:TERMINATED

2. 验证:RUNNABLE → BLOCKED(synchronized 锁竞争)

代码逻辑

两个线程竞争同一 synchronized 锁,先启动的线程持有锁,后启动的线程竞争失败进入 BLOCKED

public class ThreadStateBlocked {
    // 共享锁对象(竞争目标)
    private static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 线程 1:先获取锁,持有 2 秒
        Thread thread1 = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    System.out.println("线程 1:获取锁成功,执行中...");
                    Thread.sleep(2000); // 持有锁 2 秒,让线程 2 竞争失败
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "LockHolderThread");

        // 线程 2:后获取锁,竞争失败进入 BLOCKED
        Thread thread2 = new Thread(() -> {
            synchronized (LOCK) {
                System.out.println("线程 2:竞争锁成功,执行中...");
            }
        }, "LockWaitThread");

        // 启动线程 1,确保其先获取锁
        thread1.start();
        Thread.sleep(500); // 休眠 500ms,让线程 1 稳定持有锁

        // 启动线程 2,开始竞争锁
        thread2.start();
        Thread.sleep(500); // 休眠 500ms,让线程 2 进入 BLOCKED

        // 打印线程 2 状态:BLOCKED
        System.out.println("线程 2 状态(竞争锁失败):" + thread2.getState());

        // 等待线程 1 释放锁(2 秒后),线程 2 竞争到锁
        Thread.sleep(2000);
        System.out.println("线程 2 状态(锁释放后):" + thread2.getState());
    }
}
运行结果
线程 1:获取锁成功,执行中...
线程 2 状态(竞争锁失败):BLOCKED
线程 2:竞争锁成功,执行中...
线程 2 状态(锁释放后):TERMINATED

3. 验证:RUNNABLE → TIMED_WAITING(计时等待)

代码逻辑

线程调用 Thread.sleep(1000) 进入计时等待,期间状态为 TIMED_WAITING,超时后自动恢复 RUNNABLE

public class ThreadStateTimedWaiting {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("子线程:进入计时等待(sleep 1 秒)...");
                Thread.sleep(1000); // 触发 TIMED_WAITING
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子线程:计时等待结束,继续执行...");
        }, "TimedThread");

        thread.start();
        Thread.sleep(200); // 休眠 200ms,确保线程进入 sleep 状态

        // 打印计时等待期间状态:TIMED_WAITING
        System.out.println("子线程状态(sleep 期间):" + thread.getState());

        // 等待线程执行完毕
        thread.join();
        System.out.println("子线程状态(执行完毕):" + thread.getState());
    }
}
运行结果
子线程:进入计时等待(sleep 1 秒)...
子线程状态(sleep 期间):TIMED_WAITING
子线程:计时等待结束,继续执行...
子线程状态(执行完毕):TERMINATED
其他计时等待场景
  • Object.wait(1000):释放锁,等待 1 秒(超时自动唤醒);
  • Thread.join(1000):主线程等待子线程 1 秒(超时不再等待)。

4. 验证:RUNNABLE → WAITING(无限等待)

代码逻辑

线程 A 调用 Object.wait() 进入无限等待,线程 B 调用 Object.notify() 唤醒线程 A,恢复 RUNNABLE

public class ThreadStateWaiting {
    private static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 等待线程:调用 wait() 进入 WAITING
        Thread waitThread = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    System.out.println("等待线程:进入无限等待,等待被唤醒...");
                    LOCK.wait(); // 无参 wait(),释放锁,进入 WAITING
                    System.out.println("等待线程:被唤醒,继续执行...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "WaitThread");

        // 唤醒线程:调用 notify() 唤醒等待线程
        Thread notifyThread = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    Thread.sleep(1000); // 确保等待线程先进入 WAITING
                    System.out.println("唤醒线程:准备唤醒等待线程...");
                    LOCK.notify(); // 唤醒一个等待在 LOCK 上的线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "NotifyThread");

        waitThread.start();
        Thread.sleep(500); // 休眠 500ms,确保等待线程进入 wait()

        // 打印等待线程状态:WAITING
        System.out.println("等待线程状态:" + waitThread.getState());

        notifyThread.start();
        waitThread.join(); // 等待等待线程执行完毕
        System.out.println("等待线程最终状态:" + waitThread.getState());
    }
}
运行结果
等待线程:进入无限等待,等待被唤醒...
等待线程状态:WAITING
唤醒线程:准备唤醒等待线程...
等待线程:被唤醒,继续执行...
等待线程最终状态:TERMINATED

5. 验证:TERMINATED 状态不可逆

代码逻辑

线程终止后,尝试再次调用 start() 会抛出 IllegalThreadStateException

public class ThreadStateTerminated {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("子线程执行中...");
        }, "TerminatedThread");

        thread.start();
        thread.join(); // 等待线程终止
        System.out.println("线程终止后状态:" + thread.getState()); // TERMINATED

        // 尝试重新启动终止的线程(触发异常)
        try {
            thread.start();
        } catch (IllegalThreadStateException e) {
            System.out.println("异常信息:" + e.getMessage());
        }
    }
}
运行结果
子线程执行中...
线程终止后状态:TERMINATED
异常信息:illegal thread state

四、核心 API 与状态的关联

API 方法 作用 状态切换影响
Thread.start() 启动线程(仅能调用一次) NEW → RUNNABLE
Thread.sleep(long ms) 线程休眠(不释放锁) RUNNABLE → TIMED_WAITING
Object.wait() 无限等待(释放锁,需 synchronized RUNNABLE → WAITING
Object.wait(long ms) 计时等待(释放锁,需 synchronized RUNNABLE → TIMED_WAITING
Object.notify() 唤醒一个等待在该对象上的线程 WAITING/TIMED_WAITING → RUNNABLE
Object.notifyAll() 唤醒所有等待在该对象上的线程 WAITING/TIMED_WAITING → RUNNABLE
Thread.join() 主线程等待子线程结束(无限等待) 主线程:RUNNABLE → WAITING
Thread.join(long ms) 主线程等待子线程(计时等待) 主线程:RUNNABLE → TIMED_WAITING
LockSupport.park() 暂停线程(无超时,不释放锁) RUNNABLE → WAITING
LockSupport.unpark(Thread t) 唤醒指定线程 WAITING/TIMED_WAITING → RUNNABLE
Thread.getState() 查询线程当前状态 无(仅查询)

五、常见面试题(线程状态核心)

1. WAITING 和 TIMED_WAITING 的区别?

  • 核心差异:是否有超时时间;
  • WAITING:无超时,必须依赖其他线程唤醒(notify()/unpark());
  • TIMED_WAITING:有超时时间,超时后自动唤醒,也可手动提前唤醒。

2. BLOCKED 和 WAITING 的区别?

维度 BLOCKED WAITING
触发原因 竞争 synchronized 锁失败(被动) 调用 wait()/join() 等(主动)
锁持有情况 未持有锁(无需释放) 持有锁时会释放(如 wait()
恢复条件 锁释放并竞争成功 被唤醒或 join() 目标线程结束

3. sleep() 和 wait() 的区别?

维度 Thread.sleep(long) Object.wait()
所属类 Thread 静态方法 Object 实例方法
锁释放 不释放任何锁 释放当前对象锁(需在 synchronized 中)
唤醒方式 超时自动唤醒 需 notify()/notifyAll() 唤醒
使用场景 控制线程执行节奏(如暂停 1 秒) 线程间通信(如生产者 - 消费者模型)

4. 线程终止后能重新启动吗?

  • 不能。线程进入 TERMINATED 状态后,生命周期结束,再次调用 start() 会抛出 IllegalThreadStateException
  • 若需重复执行任务,需重新创建 Thread 对象。

六、总结

线程状态是理解多线程生命周期的核心,关键在于:

  1. 记住 6 种状态的触发条件和切换路径;
  2. 掌握核心 API 对状态的影响(如 sleep() 触发 TIMED_WAITING);
  3. 区分易混淆状态(如 BLOCKED vs WAITING)。

实际开发中,线程状态常用于问题排查(如死锁时线程处于 BLOCKED)、性能优化(如避免线程长期 WAITING),掌握这些知识能写出更稳健的多线程代码。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐