Java 线程状态

线程状态

一、操作系统的线程状态

操作系统层面,线程状态经典模型可以概括为 "三态为主,五态展开":

核心三态

  • 就绪态(Ready):线程已具备运行条件,正在等待 CPU 调度,经调度程序选中后进入 Running 状态。
  • 运行态(Running):线程正在实际占用 CPU 执行。
  • 等待态(Waiting/Blocked):线程正在等待某个事件(如锁、信号量)或资源(如 I/O)完成,即使给它 CPU 也无法继续执行。

补充两态:线程被创建、分配资源后加入就绪队列,即进入 新建态(New) ;线程执行完毕后进入 终止态(Terminated)。五态合起来就是:New → Ready → Running → Blocked → Terminated。

经典转换关系图

复制代码
         创建
          │
          ▼
      ┌───────┐
      │  New  │ (新建态)
      └───┬───┘
          │ 分配资源、加入就绪队列
          ▼
      ┌─────────┐  调度器选中   ┌─────────┐
      │ Ready   │─────────────►│ Running │
      │ (就绪态) │◄─────────────│ (运行态) │
      └────┬────┘  时间片用完   └────┬────┘
           ▲                        │
           │ 等待的事件/IO完成        │ 等待I/O、锁、信号量等
           │                        ▼
           │                  ┌──────────┐
           └──────────────────│  Blocked │
                               │ (阻塞态/等待态) │
                               └──────────┘

      Running ──执行完毕/被终止──►  ┌────────────┐
                                  │ Terminated │ (终止态)
                                  └────────────┘

关键规则:Blocked 不能直接跳到 Running,必须先回到 Ready,重新参与调度后才可能拿到 CPU。


二、Java 的线程状态

核心结论

Java 线程有 6 种状态 (Thread.State 枚举),是 JVM 层面 的状态划分,和操作系统层面的线程状态不是一一对应 的。最典型的区别是:Java 没有单独的"运行中(Running)"状态,就绪和运行中统一归为 RUNNABLE。

java 复制代码
// Thread.State 源码
public enum State {
    NEW,           // 新建状态
    RUNNABLE,      // 可运行状态
    BLOCKED,       // 阻塞状态
    WAITING,       // 等待状态
    TIMED_WAITING, // 超时等待状态
    TERMINATED     // 终止状态
}

六种状态详解

状态 含义 进入方式 典型触发 API
NEW 线程对象已创建,尚未启动 new Thread() 之后、start() 之前 ---
RUNNABLE 可运行(含"就绪"和"运行中") 调用 start() 后 ---
BLOCKED 阻塞,等待拿 synchronized 锁 争抢监视器锁失败 进入 synchronized 代码块/方法
WAITING 无限期等待,需别的线程唤醒 调用无参等待方法 Object.wait()、Thread.join()、LockSupport.park()
TIMED_WAITING 限期等待,超时自动返回 调用带超时参数的等待方法 Thread.sleep(ms)、Object.wait(ms)、Thread.join(ms)、parkNanos()
TERMINATED 线程执行结束 run() 正常返回或抛出未捕获异常 ---

状态转换图(文字版)

复制代码
NEW  ──start()──►  RUNNABLE

RUNNABLE  ──synchronized 抢锁失败──►  BLOCKED
BLOCKED   ──获得锁──►  RUNNABLE

RUNNABLE  ──wait()/join()/park()──►  WAITING
WAITING   ──notify()/notifyAll()/目标线程结束/unpark()/中断──►  RUNNABLE

RUNNABLE  ──sleep(t)/wait(t)/join(t)──►  TIMED_WAITING
TIMED_WAITING  ──超时/被唤醒/被中断──►  RUNNABLE

RUNNABLE  ──run()执行完/抛异常──►  TERMINATED

记忆技巧:所有"离开 RUNNABLE"的箭头都是主动调用某个方法造成的;所有"回到 RUNNABLE"的箭头都是被动被唤醒/超时/抢到锁造成的。


三、OS 状态 ↔ Java 状态 对照表 ⭐️

OS 状态 对应的 Java Thread.State
New NEW
Ready(就绪) RUNNABLE(就绪部分)
Running(运行) RUNNABLE(运行部分)
Blocked(等锁/等IO/等事件,统称) BLOCKED (等 synchronized 锁)或 WAITING / TIMED_WAITING(等 wait/join/park/IO)
Terminated TERMINATED

为什么会有这种差异:

  • Java 把 OS 的 Ready 和 Running 合并 成一个 RUNNABLE,因为线程调度(CPU 时间片分配)由操作系统决定,JVM 不参与、也无法感知这个粒度。
  • Java 把 OS 笼统的 Blocked 拆分 成 BLOCKED / WAITING / TIMED_WAITING 三种,是因为 JVM 想让开发者清楚"具体在等什么"------等锁、等条件、还是等超时。

四、高频考点

1. RUNNABLE 为什么不区分就绪和运行中?

线程调度(CPU 时间片分配)由操作系统决定,Java 虚拟机感知不到、也不需要感知这个粒度,所以统一算 RUNNABLE。

2. sleep() 和 wait() 的核心区别 ⭐️

对比项 Thread.sleep() Object.wait()
所属类 Thread(静态方法) Object(实例方法)
是否释放锁 不释放 释放
是否需要 synchronized 不需要 必须在 synchronized 内调用 ,否则抛 IllegalMonitorStateException
唤醒方式 超时自动醒 notify()/notifyAll() 或超时(如果用带参版本)
对应状态 TIMED_WAITING WAITING(无参)/ TIMED_WAITING(带参)
java 复制代码
synchronized (lock) {
    Thread.sleep(1000); // 持有锁睡眠,其他线程仍然 BLOCKED,拿不到锁
    lock.wait();        // 释放锁,其他线程可以进来执行
}

3. BLOCKED 和 WAITING 的区别 ⭐️(必考)

  • BLOCKED :专指等待 synchronized 内置锁。
  • WAITING / TIMED_WAITING :使用 Lock(如 ReentrantLock)、LockSupport.park()、wait()、join() 等方式等待时的状态,不算 BLOCKED。

一句话:synchronized 抢锁失败 → BLOCKED;ReentrantLock.lock() 抢锁失败 → WAITING(内部基于 AQS + LockSupport.park() 实现)。

4. 中断(interrupt)对各状态的影响

  • 线程处于 WAITING / TIMED_WAITING 时被 interrupt():会抛出 InterruptedException,线程被唤醒并回到 RUNNABLE(需要在 catch 里处理)。
  • 线程处于 RUNNABLE 时被 interrupt():不会 自动停止,只是把中断标志位置为 true,需要代码主动检查 Thread.isInterrupted() 来响应。
  • BLOCKED 状态下被中断:不会抛异常,也不会立刻退出阻塞(BLOCKED 状态对中断不敏感,直到抢到锁才会检测到中断标志)。

5. 没有"死锁"对应的状态吗?

没有专门状态。两个线程互相等待对方持有的锁,双方都会停留在 BLOCKED (或 WAITING,取决于用的锁类型),通过 jstack 可以看到线程互相 BLOCKED 并被标注 "deadlock"。

6. TERMINATED 的线程能重新 start() 吗?

不能。线程只能启动一次,重复调用 start() 会抛 IllegalThreadStateException。

7. OS 的 Blocked 能不能直接变成 Running?

不能,必须先回到 Ready(就绪队列),重新参与调度后才可能拿到 CPU 进入 Running。


五、验证代码

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

        Thread t = new Thread(() -> {
            synchronized (lock) {
                try {
                    Thread.sleep(500);   // -> TIMED_WAITING
                    lock.wait();          // -> WAITING
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });

        System.out.println(t.getState());   // NEW
        t.start();

        Thread.sleep(100);
        System.out.println(t.getState());   // TIMED_WAITING

        Thread.sleep(600);
        System.out.println(t.getState());   // WAITING

        synchronized (lock) {
            lock.notify();
        }
        t.join();
        System.out.println(t.getState());   // TERMINATED
    }
}

六、一句话总结

"操作系统层面线程有五态:New、Ready、Running、Blocked、Terminated,核心是就绪、运行、等待三态循环。Java 在此基础上做了 JVM 视角的重新划分,共 6 种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。区别主要在于两点:一是 Java 把 OS 的 Ready 和 Running 合并成了 RUNNABLE,因为 JVM 不感知 CPU 调度;二是 Java 把 OS 笼统的 Blocked 拆分成了 BLOCKED(等 synchronized 锁)和 WAITING/TIMED_WAITING(等条件、超时、IO 等),方便开发者定位问题。"


七、追问

  • 线程池中线程执行完一个任务后是什么状态?(仍是 RUNNABLE,在 getTask() 里等待新任务,不会变成 TERMINATED)
  • synchronized 和 ReentrantLock 在状态表现上的区别?(见第四节第 3 点)
  • 如何用 jstack 排查线程状态问题?(关注 BLOCKED 线程和持锁关系,排查死锁)
  • wait()、notify() 为什么要写在 synchronized 里?(保证等待和检查条件的原子性,避免"假唤醒"和竞态条件)
  • OS 的五态和七态模型区别?(七态多了 Ready Suspend / Blocked Suspend,涉及进程挂起/swap,操作系统内存管理场景才会用到,纯线程调度一般用五态即可)
相关推荐
Sweet锦5 小时前
不调 Python,不装向量库:我用纯 Java 写了一套以图搜图引擎
java·人工智能·开源·图搜索
如意猴5 小时前
【C++】007 C/C++ 内存管理机制、malloc与new的区别及模板初阶
java·c++·算法
可乐鸡翅yeah_5 小时前
HLS 分片过期清理,直播旧 TS 分片磁盘爆满问题处理
java·后端·spring·m3u8·m3u8在线·音视频在线播放
专业程序开发源6 小时前
SSM校园拍摄交流服务平台36936-计算机课程设计、毕业设计
java·spring boot·后端·python·elasticsearch·php·课程设计
用户094248568036 小时前
第23章:OpenJDK逃逸分析、标量替换与锁优化
java·jvm
wuminyu6 小时前
LockStack在虚拟线程Mount和Unmount拷贝过程剖析
java·linux·c语言·jvm·c++
樱花落木兰6 小时前
分布式登录实战:Session 会话共享改造,Redis 存储用户登录状态
java·javascript·数据库·redis·分布式·缓存
HAHAXX86 小时前
电商RPA批量上架通用方案:一套流程如何同时跑通拼多多、抖店、淘宝和跨境平台
java·运维·rpa
\光辉岁月/6 小时前
3.java运算符
java·开发语言