线程有几种状态?状态之间是如何流转的?

#Java#面试八股

线程有几种状态?状态之间是如何流转的?

结论先行:Java 线程有 6 种状态,定义在 Thread.State 枚举里:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。注意它和操作系统的线程状态不是一一对应的——JVM 故意不区分"就绪"和"运行",所以 jstack 里永远看不到 RUNNING。

1. 六种状态一览

状态怎么进入是否释放已持有的锁怎么离开
NEWnew Thread() 之后、start() 之前尚未持有调 start()
RUNNABLE调用了 start(),正在 JVM 中执行不释放被阻塞 / 等待 / 结束
BLOCKED等待 synchronized 的 monitor 进入锁还没拿到,谈不上释放抢到锁 → RUNNABLE
WAITINGwait()、join()、park(),且无限等wait() 会释放,join/park 不涉及notify/notifyAll/被 join 的线程结束
TIMED_WAITINGsleep(n)、wait(n)、join(n)、parkNanossleep(n) 不释放,wait(n) 释放超时或被唤醒
TERMINATEDrun() 正常返回或抛出未捕获异常已释放不可逆

2. 状态流转图

NEW ──start()──▶ RUNNABLE

RUNNABLE ──等 synchronized 进入锁──▶ BLOCKED ──获得锁──▶ RUNNABLE

RUNNABLE ──wait()/join()/park()(无超时)──▶ WAITING
WAITING  ──notify()/notifyAll()/被 join 线程结束──▶ RUNNABLE

RUNNABLE ──sleep(n)/wait(n)/join(n)/parkNanos──▶ TIMED_WAITING
TIMED_WAITING ──超时或提前被唤醒──▶ RUNNABLE

RUNNABLE ──run() 返回或抛异常──▶ TERMINATED

三个容易记混的点:

  1. 只有等 synchronized 才是 BLOCKED;等 ReentrantLock、等 Condition、等 IO 都是 WAITING 或 TIMED_WAITING;
  2. sleep() 不释放锁,wait() 释放锁——这是 wait/sleep 最本质的差别;
  3. 线程一旦 TERMINATED,再调 start() 会抛 IllegalThreadStateException,不能复活。

3. 用代码把状态"抓"出来

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

    public static void main(String[] args) throws Exception {
        Thread t = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    LOCK.wait();               // 进入 WAITING 并释放 LOCK
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "state-demo");

        System.out.println(t.getState());      // NEW
        t.start();
        Thread.sleep(100);
        System.out.println(t.getState());      // WAITING
        synchronized (LOCK) { LOCK.notifyAll(); }
        t.join();
        System.out.println(t.getState());      // TERMINATED
    }
}

线上更常用的是 jstack,它把状态翻译成了人话:

"state-demo" #23 prio=5 os_prio=0 tid=0x00007f... nid=0x6a2c in Object.wait()
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at com.demo.StateDemo.lambda$main$0(StateDemo.java:11)
        - waiting on <0x000000076ab622b8> (a java.lang.Object)
        - locked <0x000000076ab622a8> (a java.lang.Object)

locked 表示已持有、waiting on 表示在等它,一眼就能定位"谁堵住了谁"。

4. 常见追问

追问一:为什么没有 RUNNING 状态? 因为"是否正在占用 CPU"由操作系统调度,JVM 不去实时同步这个信息(同步成本高且没有意义)。所以 RUNNABLE 是一个大杂烩:可能在跑、可能在就绪队列里排队、也可能阻塞在 read() 这类 native IO 上——后者在读 socket 时同样显示 RUNNABLE,排查时不要被它骗了。

追问二:BLOCKED 和 WAITING 的区别? BLOCKED 是被动的:想进 synchronized 但锁在别人手里,只能等;WAITING 是主动的:自己调了 wait()/park() 让出执行权,且 wait() 会释放锁。前者是"抢不到",后者是"自己不干了"。

追问三:Thread.sleep 与 Object.wait 有什么区别?

对比项sleep(n)wait(n)
所属类Thread 静态方法Object 实例方法
是否释放锁不释放释放
是否必须在同步块不需要必须持有该对象的 monitor
唤醒方式只有超时超时或被 notify
状态TIMED_WAITINGTIMED_WAITING 或 WAITING

追问四:线程状态能用于业务判断吗? 不能。getState() 是瞬时快照,读到 RUNNABLE 下一纳秒就可能变。它只适合排查与监控,不能当业务条件使用。

小结

记状态不要死背,抓住"等锁是 BLOCKED,等信号是 WAITING,等时间是 TIMED_WAITING"这条主线,再补一句"wait 释放锁、sleep 不释放",线程状态这块的追问基本都能接住。

笔记加载中…