2. 线程生命周期状态观测
线程状态迁移的触发条件
Java 线程在 Thread.getState() 中返回 Thread.State 枚举,共 6 个值。要观测到全部状态,关键是构造能触发每种迁移的代码路径:
- NEW:
new Thread(...)之后、start()之前。 - RUNNABLE:调用
start()后,线程已就绪或正在运行(JVM 不区分就绪与运行)。 - BLOCKED:等待进入
synchronized临界区,锁被其他线程持有。 - WAITING:调用
Object.wait()、Thread.join()、LockSupport.park()等无超时等待。 - TIMED_WAITING:调用
Thread.sleep(ms)、wait(ms)、join(ms)等带超时等待。 - TERMINATED:
run()正常返回或抛出未捕获异常后。
观测程序实现
下面的程序用一个观测线程每 50ms 打印目标线程状态,目标线程依次经历 6 种状态。为了让 BLOCKED 稳定出现,先让一个“持锁线程”占用监视器锁 800ms,目标线程随后尝试进入同一把锁的同步块。
public class ThreadStateDemo {
static final Object LOCK = new Object();
public static void main(String[] args) throws Exception {
Thread target = new Thread(() -> {
try {
// 1. TIMED_WAITING:sleep 500ms
Thread.sleep(500);
// 2. BLOCKED:等待 LOCK 监视器
synchronized (LOCK) {
// 3. WAITING:无超时 wait,需被 notify 唤醒
LOCK.wait();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "target-thread");
// 观测线程:每 50ms 打印一次状态
Thread observer = new Thread(() -> {
Thread.State last = null;
long deadline = System.currentTimeMillis() + 4000;
while (System.currentTimeMillis() < deadline) {
Thread.State now = target.getState();
if (now != last) {
System.out.printf("[%6d ms] %s%n",
System.currentTimeMillis() % 100000, now);
last = now;
}
try { Thread.sleep(50); } catch (InterruptedException e) { return; }
}
}, "observer");
System.out.println("start 前: " + target.getState()); // NEW
observer.start();
target.start(); // -> RUNNABLE
// 持锁线程:先占住 LOCK 800ms,制造 BLOCKED
Thread holder = new Thread(() -> {
synchronized (LOCK) {
try { Thread.sleep(800); } catch (InterruptedException ignored) {}
}
}, "lock-holder");
holder.start();
// 等目标线程进入 WAITING 后唤醒它
Thread.sleep(2000);
synchronized (LOCK) {
LOCK.notifyAll(); // 唤醒 target,使其从 WAITING 返回
}
target.join(); // 主线程等待 target 结束
observer.join();
System.out.println("结束后: " + target.getState()); // TERMINATED
}
}
状态迁移路径与观测点
下图给出本程序实际触发的状态迁移链,以及每个状态对应的触发代码和观测手段。注意 getState() 是瞬时快照,观测线程的采样间隔决定了能否捕获短状态。
运行结果与判读
示例输出(时间戳为相对毫秒,实际数值随调度略有浮动):
start 前: NEW
[ 102 ms] RUNNABLE
[ 152 ms] TIMED_WAITING
[ 652 ms] BLOCKED
[ 1452 ms] WAITING
[ 2052 ms] TERMINATED
结束后: TERMINATED
判读要点:
- NEW 只在
start()前出现,一旦start()返回就不可再回到 NEW。 - BLOCKED 出现在 sleep 结束之后、进入
synchronized之前,因为此时持锁线程仍占用 LOCK。 - WAITING 出现在获得锁并调用
LOCK.wait()之后,直到主线程notifyAll()才被唤醒。 - TERMINATED 是终态,
getState()之后永远返回 TERMINATED。
若观测不到 BLOCKED,通常是持锁时间太短或目标线程先拿到了锁。可把持锁线程的 sleep 调大,或在目标线程进入同步块前加一个短暂 sleep 来稳定复现。
不要在
synchronized 块内调用 Thread.sleep() 来制造 BLOCKED,那会让当前线程进入 TIMED_WAITING 而非 BLOCKED。BLOCKED 只发生在“尝试获取监视器锁但未获得”的瞬间。