2. 线程生命周期状态观测

线程生命周期状态观测 学习卡片

线程状态观测的核心思路

Java 线程在 Thread.State 枚举中定义了 6 种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。要观测这些状态,关键是让线程在受控条件下进入每种状态,再用 getState() 读取,并用线程转储(jstack 或 Thread.getAllStackTraces())交叉验证。

Java 线程六状态转换与观测触发条件 NEW new Thread() 后 RUNNABLE start() 后 / 就绪+运行 TERMINATED run() 正常/异常结束 BLOCKED 等待 synchronized 锁 WAITING wait() / join() / park() TIMED_WAITING sleep(ms) / wait(ms) start() run() 返回 竞争锁失败 获得锁 wait()/join() notify()/被join线程结束 sleep(ms)/wait(ms) 超时/notify() 观测手段与验证方法 getState() 轮询 主线程循环读取目标线程状态 适合观测状态切换瞬间 Thread.getAllStackTraces() 程序内获取所有线程栈 可看到线程名与状态 jstack <pid> 外部命令行线程转储 显示 java.lang.Thread.State

让线程进入各状态的受控代码

下面这段程序用一个共享锁对象和多个线程,依次触发 BLOCKED、WAITING、TIMED_WAITING 状态,并在主线程中轮询打印状态变化。

import java.util.concurrent.TimeUnit;

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

    public static void main(String[] args) throws Exception {
        // 1. NEW 状态
        Thread t = new Thread(() -> {
            // 3. RUNNABLE 状态(进入 run 后)
            synchronized (LOCK) {
                try {
                    // 4. WAITING 状态:wait() 无超时
                    LOCK.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }, "state-demo");
        System.out.println("[1] 创建后: " + t.getState()); // NEW

        // 2. start() 后进入 RUNNABLE
        t.start();
        Thread.sleep(50);
        System.out.println("[2] start后: " + t.getState()); // RUNNABLE 或 WAITING

        // 5. 让另一个线程持有锁,使 t 进入 BLOCKED
        Thread holder = new Thread(() -> {
            synchronized (LOCK) {
                try { Thread.sleep(800); } catch (InterruptedException ignored) {}
            }
        }, "lock-holder");
        holder.start();
        Thread.sleep(100);
        System.out.println("[3] 锁被占时: " + t.getState()); // BLOCKED

        // 6. TIMED_WAITING 演示
        Thread sleeper = new Thread(() -> {
            try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
        }, "sleeper");
        sleeper.start();
        Thread.sleep(100);
        System.out.println("[4] sleep中: " + sleeper.getState()); // TIMED_WAITING

        // 7. 唤醒 t,观察回到 RUNNABLE 再到 TERMINATED
        synchronized (LOCK) { LOCK.notifyAll(); }
        t.join();
        System.out.println("[5] 结束后: " + t.getState()); // TERMINATED

        // 8. 线程转储验证
        Thread.getAllStackTraces().forEach((th, stack) -> {
            if (th.getName().startsWith("state-demo") || th.getName().startsWith("sleeper")) {
                System.out.println("转储: " + th.getName() + " -> " + th.getState());
            }
        });
    }
}
关键点:RUNNABLE 在 Java 中同时涵盖“就绪”和“运行中”两种操作系统层面状态,因为 JVM 不区分线程是否已获得 CPU 时间片。因此观测时看到 RUNNABLE 并不代表线程一定在执行,也可能在等待 CPU 调度。

状态转换条件速查

起始状态触发动作目标状态恢复条件
NEW调用 start()RUNNABLE—
RUNNABLE竞争 synchronized 锁失败BLOCKED持有锁的线程释放锁
RUNNABLE调用 wait() / join() / LockSupport.park()WAITINGnotify()/notifyAll() 或被 join 线程结束
RUNNABLE调用 sleep(ms) / wait(ms) / join(ms)TIMED_WAITING超时到期或被唤醒
RUNNABLErun() 正常返回或抛出未捕获异常TERMINATED不可逆
注意:BLOCKED 仅针对 synchronized 锁竞争。使用 ReentrantLock 等 java.util.concurrent 锁时,等待锁的线程状态是 WAITING 或 TIMED_WAITING,而非 BLOCKED。这是观测时容易混淆的地方。

验证与判读

  • 运行程序后,控制台应依次输出 NEW → RUNNABLE/WAITING → BLOCKED → TIMED_WAITING → TERMINATED 的状态序列。
  • 若 [3] 处输出不是 BLOCKED,检查 holder 线程是否已先于 t 获得锁,可适当增大 Thread.sleep(100) 的等待时间。
  • 用 jstack <pid> 在程序运行期间抓取转储,搜索线程名 state-demo,应能看到 java.lang.Thread.State: WAITING (on object monitor) 或 BLOCKED (on object monitor) 字样。
  • 线程终止后再次调用 getState() 始终返回 TERMINATED,且 isAlive() 返回 false,可作为终止状态的辅助判据。