在 Java 并发编程中,线程(Thread) 是最核心的执行单元。很多开发者在学习多线程时,会被各种状态(NEW、RUNNABLE、WAITING 等)搞晕:线程到底有哪些生命周期?这些状态是怎么转换的?本文将结合 操作系统原理 + Java 语言规范 ,带你完整理解 线程的生命周期与状态转换。
一、线程的五大生命周期
Java 线程大体可以分为 五种生命周期:
-
新建(NEW)
- 当使用
new Thread(...)
创建线程对象时,线程处于新建状态。 - 此时线程尚未启动,只有调用
start()
方法,线程才会进入可运行状态。
- 当使用
-
可运行(RUNNABLE)
- 调用
start()
之后,线程进入可运行状态。 - 此时线程可能正在 等待 CPU 调度,也可能正在运行。
- 注意:Java 把"就绪(Ready)"和"运行中(Running)"都统称为 RUNNABLE。
- 调用
-
阻塞(BLOCKED)
- 当线程尝试获取 synchronized 锁 但未成功时,会进入 BLOCKED 状态,直到获得锁。
-
等待(WAITING / TIMED_WAITING)
- 调用
Object.wait()
、Thread.join()
或LockSupport.park()
会进入 WAITING(无限期等待) 状态; - 调用
sleep(ms)
、wait(ms)
、join(ms)
、parkNanos()
等,会进入 TIMED_WAITING(限时等待) 。
- 调用
-
终止(TERMINATED)
- 线程执行完
run()
方法后,生命周期结束,进入终止状态。 - 已终止的线程不能再次启动。
- 线程执行完
二、线程状态转换图
可以用一个简化的状态图来理解线程生命周期:
这张图非常直观地展示了线程在 Java 中的状态切换路径。
三、常见场景下的状态变化
1. start()
之后
java
Thread t = new Thread(() -> System.out.println("hello"));
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE
2. sleep()
进入 TIMED_WAITING
java
Thread t = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
Thread.sleep(100); // 主线程先睡一下
System.out.println(t.getState()); // TIMED_WAITING
3. wait()
进入 WAITING
java
Object lock = new Object();
Thread t = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(); // 无限等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(100);
System.out.println(t.getState()); // WAITING
4. synchronized
竞争进入 BLOCKED
java
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
try { Thread.sleep(2000); } catch (InterruptedException e) {}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("t2 acquired lock");
}
});
t1.start();
Thread.sleep(100); // 确保t1先拿到锁
t2.start();
Thread.sleep(100);
System.out.println(t2.getState()); // BLOCKED
四、操作系统视角 vs Java 视角
-
操作系统层面
- 线程会经历:新建、就绪(Ready)、运行(Running)、阻塞(Blocked)、终止(Terminated)。
- 就绪和运行是分开的,因为 CPU 调度才会切换。
-
Java 层面
- JDK 把 Ready + Running 合并为 RUNNABLE。
- 引入 WAITING 和 TIMED_WAITING 区分不同等待场景。
- 因此,Java 的 Thread.State 更偏向于开发者语义,而非操作系统真实调度。
五、开发中常见误区
-
误区一:线程调用
run()
就能启动- 错 ❌:
run()
只是普通方法调用; - 对 ✔️:必须调用
start()
才能启动新线程。
- 错 ❌:
-
误区二:BLOCKED 和 WAITING 一样
- 错 ❌:BLOCKED 是锁竞争,WAITING 是主动挂起等待。
- 对 ✔️:两者触发条件和唤醒机制完全不同。
-
误区三:线程终止后还能
start()
- 错 ❌:会抛
IllegalThreadStateException
。 - 对 ✔️:线程只能运行一次,想再执行要创建新对象。
- 错 ❌:会抛
六、总结
- Java 线程生命周期包含 NEW → RUNNABLE → {BLOCKED, WAITING, TIMED_WAITING} → TERMINATED。
- 状态转换取决于 锁竞争、sleep、wait、join、park 等操作。
- Java 的状态模型是对操作系统模型的抽象,简化了开发者理解。
理解线程生命周期,不仅能帮助我们更好地写并发程序,还能在调试时快速定位问题。例如,遇到线程卡死,可以通过 jstack
查看线程是否处于 BLOCKED (锁竞争)或 WAITING(等待唤醒)。