线程状态

一、操作系统的线程状态

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

核心三态

  • 就绪态(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。

// 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()──► WAITING
WAITING ──notify/notifyAll/目标线程结束/中断──► BLOCKED ──获得锁──► RUNNABLE

RUNNABLE ──park()──► WAITING
WAITING ──unpark/中断──► RUNNABLE

RUNNABLE ──sleep(t)/wait(t)/join(t)/parkNanos(t)──► TIMED_WAITING

TIMED_WAITING ──sleep 超时/中断──► RUNNABLE
TIMED_WAITING ──wait(t)/join(t) 超时/notify/中断──► BLOCKED ──获得锁──► RUNNABLE
TIMED_WAITING ──parkNanos 超时/unpark/中断──► RUNNABLE

RUNNABLE ──run() 结束/异常退出──► TERMINATED
                    start()
NEW ─────────────────────────► RUNNABLE
                                  │
             ┌────────────────────┼─────────────────────┐
             │                    │                     │
       抢 synchronized 锁失败    wait() / join()       park()
             │                    │                     │
             ▼                    ▼                     ▼
          BLOCKED              WAITING              WAITING
             │                    │                     │
          获得锁            notify / join结束       unpark
             │                    │                     │
             └──────────────► RUNNABLE ◄───────────────┘


RUNNABLE
   │
   ├── sleep(t) ──────────────► TIMED_WAITING
   │                              │
   │                           超时/中断
   │                              │
   │                              ▼
   │                           RUNNABLE
   │
   ├── wait(t) ───────────────► TIMED_WAITING
   │                              │
   │                       超时/notify/中断
   │                              ▼
   │                           BLOCKED
   │                              │
   │                           获得锁
   │                              ▼
   │                           RUNNABLE
   │
   ├── join(t) ───────────────► TIMED_WAITING
   │                              │
   │                       超时/目标线程结束
   │                              ▼
   │                           RUNNABLE
   │
   └── parkNanos(t) ─────────► TIMED_WAITING
                                  │
                             超时/unpark/中断
                                  ▼
                               RUNNABLE


RUNNABLE
   │
   └── run()结束/异常
              ▼
         TERMINATED

可以。面试时建议不要只背“状态变化”,而是从 作用、是否释放锁、如何唤醒、是否抛异常 四个维度记忆。

Java 线程常见 API 面试速记表

API作用状态变化是否释放锁如何恢复是否抛 InterruptedException
sleep(t)让当前线程休眠一段时间RUNNABLE → TIMED_WAITING → RUNNABLE不释放锁时间到是
wait()当前线程等待,直到其他线程通知RUNNABLE → WAITING → BLOCKED → RUNNABLE释放当前对象锁notify/notifyAll 或中断是
wait(t)最多等待指定时间RUNNABLE → TIMED_WAITING → BLOCKED → RUNNABLE释放当前对象锁超时 / notify / 中断是
notify()唤醒一个正在该对象上 wait() 的线程WAITING → BLOCKED不立即释放锁被唤醒线程重新获得锁后继续否
notifyAll()唤醒所有正在该对象上 wait() 的线程WAITING → BLOCKED不立即释放锁竞争锁成功后继续否
join()当前线程等待另一个线程执行结束RUNNABLE → WAITING → RUNNABLE通常不涉及释放当前业务锁目标线程结束是
join(t)最多等待另一个线程指定时间RUNNABLE → TIMED_WAITING → RUNNABLE通常不涉及释放当前业务锁目标线程结束 / 超时是
park()挂起当前线程RUNNABLE → WAITING不释放 Monitor 锁unpark() / 中断否
parkNanos(t)挂起当前线程指定时间RUNNABLE → TIMED_WAITING不释放 Monitor 锁超时 / unpark() / 中断否
unpark(thread)允许指定线程从 park() 中恢复WAITING → RUNNABLE不涉及unpark()否

重点区别,面试直接背

对比sleep()wait()park()
核心作用休眠等待通知挂起线程
所属ThreadObjectLockSupport
是否释放锁❌✅❌
是否需要持有锁❌必须持有对象 Monitor 锁❌
谁唤醒时间到notify/notifyAllunpark()
中断抛 InterruptedException抛 InterruptedException返回,不抛异常

join() 一句话理解

threadA.join();

就是:

当前线程暂停执行,等待 threadA 执行结束。

例如:

主线程
  │
  ├── threadA.start()
  │
  ├── threadA.join()
  │       ↓
  │   等待 threadA
  │
  └── threadA结束后继续执行

wait/notify 最容易被问的细节

synchronized (obj) {
    obj.wait();
}

wait():

释放锁 + 进入等待

synchronized (obj) {
    obj.notify();
}

notify():

唤醒一个等待线程,但不会立即释放锁。

所以实际过程是:

线程 A:
持有 obj 锁
   ↓
wait()
   ↓
释放 obj 锁
   ↓
WAITING


线程 B:
获得 obj 锁
   ↓
notify()
   ↓
A 被唤醒
   ↓
A 进入 BLOCKED
   ↓
等待重新获得 obj 锁
   ↓
RUNNABLE

API 的作用

Thread.sleep():让当前线程休眠,不释放已持有的锁。
object.wait():让当前线程等待,释放当前对象的 Monitor 锁。
object.notify():唤醒一个等待线程,但不会立即释放当前持有的锁。
thread.join():让当前线程等待 thread 执行结束。
LockSupport.park():挂起当前线程,可以通过 unpark(thread) 恢复。

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


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

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

为什么会有这种差异:

  • 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_WAITINGWAITING(无参)/ TIMED_WAITING(带参)
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。


五、验证代码

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,操作系统内存管理场景才会用到,纯线程调度一般用五态即可)
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐