首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【线程的几种状态】

【线程的几种状态】

作者头像
艾伦耶格尔
发布2025-08-28 15:59:22
发布2025-08-28 15:59:22
3420
举报
文章被收录于专栏:Java基础Java基础

💡 摘要:你是否曾被 RUNNABLE 状态迷惑——它为何包含了“阻塞”? 是否在排查线程池时,看到 WAITING 状态却不知其因? Java 线程的 6 种状态(NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED) 不仅是枚举值,更是理解 JVM 线程模型、锁竞争、协作机制的钥匙。 本文将从基础到深入,结合代码、图解、jstack 实战, 彻底讲清每种状态的含义、转换条件、监控方法与常见误区。 文末附高清状态转换图面试高频问题清单,助你真正掌握多线程核心基础。


一、从“线程是什么”说起

在深入状态前,先明确:线程是 CPU 调度的基本单位

  • 一个 Java 程序启动,JVM 会创建一个 main 线程。
  • 你可以创建更多线程,让任务并发执行。
  • 但 CPU 核心数有限,操作系统需要在多个线程间快速切换,造成“同时运行”的假象。

🔑 线程状态的本质: 描述线程在 JVM 和操作系统调度 下的当前所处阶段


二、Java 线程状态全景

Java 定义了 6 种线程状态,位于 java.lang.Thread.State 枚举中:

代码语言:javascript
复制
public enum State {
    NEW,           // 新建
    RUNNABLE,      // 可运行
    BLOCKED,       // 阻塞
    WAITING,       // 无限期等待
    TIMED_WAITING, // 限期等待
    TERMINATED     // 终止
}

重要提示: 这是 JVM 层面的抽象状态,与操作系统的线程状态(如 running, ready, waiting)不完全等价。 特别是 RUNNABLE,它包含了操作系统的 ready(就绪)和 running(运行)两种状态。


三、逐个击破:6 种状态详解

1. NEW(新建)——生命的起点

  • 含义:线程对象已创建,但尚未调用 start()
  • 特点
    • 线程还未被 JVM 纳入调度。
    • 此时调用 getState() 可返回 NEW
代码语言:javascript
复制
Thread t = new Thread(() -> System.out.println("Hello"));
System.out.println(t.getState()); // 输出:NEW
t.start(); // 调用后,状态将改变

生命周期的第一步


2. RUNNABLE(可运行)——CPU 的“候选者”

  • 含义:线程正在 JVM 中执行,或正等待 CPU 分配时间片
  • 关键理解(易错点!)
    • RUNNABLE ≠ 正在运行
    • 它包含两种 OS 状态:
      1. ready:已准备好,排队等 CPU
      2. running:正在 CPU 上执行
    • I/O 阻塞(如读文件、网络请求)也属于 RUNNABLE! 因为 JVM 无法区分线程是在计算还是在等 I/O。
代码语言:javascript
复制
// 示例 1:CPU 密集型(running)
Thread cpuTask = new Thread(() -> {
    long sum = 0;
    for (int i = 0; i < Integer.MAX_VALUE; i++) sum++;
}); // 状态:RUNNABLE

// 示例 2:I/O 操作(看似阻塞,但状态仍是 RUNNABLE)
Thread ioTask = new Thread(() -> {
    try (FileInputStream fis = new FileInputStream("large.log")) {
        fis.readAllBytes(); // I/O 阻塞中,但 getState() == RUNNABLE
    } catch (IOException e) { e.printStackTrace(); }
});

🔥 面试常考:为什么 I/O 阻塞的线程状态是 RUNNABLE:JVM 的线程状态模型未细分 I/O 阻塞,只要线程未进入 wait/sleep/等锁,就视为“可运行”。


3. BLOCKED(阻塞)——锁的竞争者

  • 含义:线程试图获取一个监视器锁(monitor)失败,等待进入 synchronized 块/方法。
  • 触发场景
    • 尝试进入一个被其他线程持有的 synchronized 方法或代码块。
    • 从 WAITING 状态被唤醒后,重新竞争锁失败。
代码语言:javascript
复制
Object lock = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lock) {
        System.out.println("t1 持有锁,开始休眠...");
        try { Thread.sleep(3000); } catch (InterruptedException e) {}
        System.out.println("t1 释放锁");
    }
});

Thread t2 = new Thread(() -> {
    System.out.println("t2 尝试获取锁...");
    synchronized (lock) { // t2 在此处进入 BLOCKED 状态
        System.out.println("t2 终于获得锁!");
    }
});

t1.start();
Thread.sleep(500);
t2.start(); // t2 会因锁被 t1 占有而进入 BLOCKED

BLOCKED 的核心是 synchronized 锁的竞争


4. WAITING(无限期等待)——协作的等待者

  • 含义:线程无限期等待另一个线程执行特定操作来唤醒它。
  • 进入方式(调用以下方法,无超时参数):
    • Object.wait()
    • Thread.join()(无超时)
    • LockSupport.park()
  • 唤醒方式
    • Object.notify() / notifyAll()
    • 被其他线程中断(interrupt()
    • LockSupport.unpark()
代码语言:javascript
复制
Thread t1 = new Thread(() -> {
    synchronized (lock) {
        try {
            System.out.println("t1 进入等待...");
            lock.wait(); // t1 进入 WAITING 状态,释放锁
            System.out.println("t1 被唤醒!");
        } catch (InterruptedException e) { e.printStackTrace(); }
    }
});

Thread t2 = new Thread(() -> {
    synchronized (lock) {
        System.out.println("t2 即将唤醒 t1");
        lock.notify(); // 唤醒 t1
    }
});

t1.start();
Thread.sleep(1000);
t2.start();

WAITING 状态的线程不消耗 CPU,是线程间协作的基础。


5. TIMED_WAITING(限期等待)——有耐心的等待者

  • 含义:线程等待一段时间,时间到后自动唤醒。
  • 进入方式(带超时参数):
    • Thread.sleep(long millis)
    • Object.wait(long timeout)
    • Thread.join(long millis)
    • LockSupport.parkNanos(long nanos)
  • 唤醒方式
    • 超时自动唤醒
    • 被中断
    • 显式唤醒(如 notify
代码语言:javascript
复制
Thread t = new Thread(() -> {
    try {
        System.out.println("开始休眠 2 秒...");
        Thread.sleep(2000); // 进入 TIMED_WAITING
        System.out.println("休眠结束!");
    } catch (InterruptedException e) { e.printStackTrace(); }
});

TIMED_WAITING 是 WAITING 的“限时版”


6. TERMINATED(终止)——生命的终点

  • 含义:线程的 run() 方法已执行完毕,或因未捕获异常而退出。
  • 特点
    • 线程对象依然存在,但已“死亡”。
    • 无法再次 start()(会抛 IllegalThreadStateException)。
代码语言:javascript
复制
Thread t = new Thread(() -> {
    System.out.println("任务完成");
});
t.start();
t.join(); // 等待 t 结束
System.out.println(t.getState()); // 输出:TERMINATED

四、线程状态转换图(高清版)

核心逻辑

  • RUNNABLE 是中心枢纽。
  • BLOCKED 专为 synchronized 锁设计。
  • WAITING 和 TIMED_WAITING 用于线程协作。

五、实战:如何监控线程状态?

1. 代码中获取状态:getState()

代码语言:javascript
复制
Thread t = new Thread(() -> {
    try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
System.out.println("NEW: " + t.getState());
t.start();
System.out.println("启动后: " + t.getState()); // 可能是 RUNNABLE
Thread.sleep(100);
System.out.println("休眠中: " + t.getState()); // TIMED_WAITING
t.join();
System.out.println("结束后: " + t.getState()); // TERMINATED

⚠️ 注意:getState() 是瞬时快照,可能不精确。


2. 生产环境利器:jstack

代码语言:javascript
复制
# 1. 查找 Java 进程 ID
jps

# 2. 输出线程栈和状态
jstack <pid>

输出片段:

代码语言:javascript
复制
"main" #1 prio=5 os_prio=0 cpu=15.62ms elapsed=4.32s tid=0x000002aab8019000 nid=0x5a44 waiting on condition  [0x000000b8c5fff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(java.base@17.0.8/Native Method)
        at Main.main(Main.java:5)

🔥 jstack 是诊断死锁、性能瓶颈的必备工具。


3. 线程池中的状态

线程池中的工作线程(worker)在空闲时会调用 workQueue.take(),进入 WAITING 状态,等待新任务。


六、常见问题与面试解析

❓1. RUNNABLE 为什么包含 I/O 阻塞?

:JVM 的线程状态模型是简化的。 只要线程没有调用 waitsleep 或进入 synchronized 竞争锁, 即使它在等待 I/O,JVM 也认为它处于“可运行”状态。 这也是 jstack 中大量线程显示 RUNNABLE 的原因。


❓2. BLOCKED 和 WAITING 的本质区别?

  • BLOCKED:因锁竞争失败而被动阻塞(synchronized)。
  • WAITING:因主动协作而等待(wait/join),需其他线程显式唤醒。
  • 关键WAITING 会释放锁,BLOCKED 不会。

❓3. sleep() 和 wait() 的状态与锁行为?

: 方法状态是否释放锁调用位置sleep()TIMED_WAITING否任意位置wait()WAITING是必须在 synchronized 块内


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

不能。 线程是“一次性”的。终止后再次 start() 会抛 IllegalThreadStateException。 应使用线程池或创建新线程。


❓5. 如何判断线程是否活跃?

:使用 thread.isAlive()。 当线程处于 RUNNABLE, BLOCKED, WAITING, TIMED_WAITING 时返回 true


七、总结

状态

触发条件

关键点

NEW

new Thread()

未启动

RUNNABLE

start() / I/O / 计算

包含就绪、运行、I/O 阻塞

BLOCKED

synchronized 竞争锁

锁竞争

WAITING

wait() / join() (无超时)

主动协作,需唤醒

TIMED_WAITING

sleep() / wait(timeout)

限期等待,自动唤醒

TERMINATED

run() 结束

生命周期结束

掌握线程状态,是理解并发编程的第一步。 动手实践,结合 jstack 分析,你将对多线程有更深的理解。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、从“线程是什么”说起
  • 二、Java 线程状态全景
  • 三、逐个击破:6 种状态详解
    • 1. NEW(新建)——生命的起点
    • 2. RUNNABLE(可运行)——CPU 的“候选者”
    • 3. BLOCKED(阻塞)——锁的竞争者
    • 4. WAITING(无限期等待)——协作的等待者
    • 5. TIMED_WAITING(限期等待)——有耐心的等待者
    • 6. TERMINATED(终止)——生命的终点
  • 四、线程状态转换图(高清版)
  • 五、实战:如何监控线程状态?
    • 1. 代码中获取状态:getState()
    • 2. 生产环境利器:jstack
    • 3. 线程池中的状态
  • 六、常见问题与面试解析
    • ❓1. RUNNABLE 为什么包含 I/O 阻塞?
    • ❓2. BLOCKED 和 WAITING 的本质区别?
    • ❓3. sleep() 和 wait() 的状态与锁行为?
    • ❓4. 线程终止后能重启吗?
    • ❓5. 如何判断线程是否活跃?
  • 七、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档