Java多线程(二):线程的生命周期与状态转换
在上一篇文章《Java多线程(一):多线程的三种创建方式》中,我们学习了如何通过继承 Thread
类、实现 Runnable
接口以及使用 Callable
和 FutureTask
三种方式来创建线程。创建线程只是第一步,理解线程在运行过程中的生命周期和状态转换,是掌握多线程编程的关键。
本文将深入探讨 Java 线程的六种状态、它们之间的转换关系,以及影响状态变化的核心方法,帮助你构建对线程运行机制的完整认知。
一、Java线程的六种状态
在 Java 中,线程的生命周期由 java.lang.Thread.State
枚举类定义,共包含六种状态:
状态 | 说明 |
---|---|
NEW | 新建状态。线程对象已创建,但尚未调用 start() 方法。 |
RUNNABLE | 可运行状态。线程已启动,正在 JVM 中执行,或等待操作系统调度 CPU 时间片。 |
BLOCKED | 阻塞状态。线程等待获取一个监视器锁(monitor lock),以进入同步代码块或方法。 |
WAITING | 无限等待状态。线程主动等待,直到被其他线程显式唤醒(如 notify() 或 notifyAll() )。 |
TIMED_WAITING | 限时等待状态。线程在指定时间内等待,时间到后自动恢复。 |
TERMINATED | 终止状态。线程执行完毕或因异常退出。 |
💡 注意:
RUNNABLE
状态实际上包含了操作系统层面的“就绪”和“运行”两种状态,Java 线程模型将其统一视为可运行。
二、线程状态转换图
下图展示了线程在不同操作下的状态转换关系:
+-----------------+
| NEW |
+--------+--------+
|
| start()
v
+-----------------+
| RUNNABLE |
+--------+--------+
|
+-------------------------+-------------------------+
| | |
| wait() | sleep(), join(), | synchronized
| wait(timeout) | wait(timeout) | block
v v v
+--------------+ +---------------------+ +--------------+
| WAITING | | TIMED_WAITING | | BLOCKED |
+------+-------+ +----------+----------+ +------+-------+
| | |
| notify()/notifyAll() | timeout / interrupt() | acquire lock
+-------------------------+------------------------+
|
|
v
+-----------------+
| TERMINATED |
+-----------------+
下面我们通过代码示例,逐一解析每种状态的触发条件和转换过程。
三、状态详解与代码示例
1. NEW → RUNNABLE
当线程对象创建后,调用 start()
方法,线程进入 RUNNABLE
状态。
Thread thread = new Thread(() -> {
System.out.println("线程正在运行");
});
System.out.println("创建后状态: " + thread.getState()); // NEW
thread.start();
System.out.println("启动后状态: " + thread.getState()); // 很可能是 RUNNABLE
⚠️ 注意:
start()
调用后,线程不一定立即执行,因此状态可能是RUNNABLE
。
2. RUNNABLE → BLOCKED
当多个线程竞争同一个同步锁时,未获得锁的线程会进入 BLOCKED
状态。
Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(5000); // 持有锁5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("t2 获取到锁");
}
});
t1.start();
t2.start();
// 观察 t2 的状态
new Thread(() -> {
while (t2.isAlive()) {
System.out.println("t2 当前状态: " + t2.getState()); // BLOCKED
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
输出中会看到 t2 在等待锁时处于 BLOCKED
状态。
3. RUNNABLE → WAITING
线程调用 wait()
、join()
(无参)、LockSupport.park()
时,会进入无限等待状态,必须由其他线程调用 notify()
或 notifyAll()
才能唤醒。
Thread waiter = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("进入等待...");
lock.wait(); // 进入 WAITING 状态
System.out.println("被唤醒!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread notifier = new Thread(() -> {
synchronized (lock) {
System.out.println("准备唤醒...");
lock.notify();
}
});
waiter.start();
Thread.sleep(1000);
notifier.start();
使用 jstack
或监控线程状态可观察到 waiter
线程处于 WAITING
状态。
4. RUNNABLE → TIMED_WAITING
调用以下方法时,线程进入限时等待:
Thread.sleep(long millis)
wait(long timeout)
join(long millis)
LockSupport.parkNanos()
Thread sleeper = new Thread(() -> {
try {
Thread.sleep(3000); // 进入 TIMED_WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
});
sleeper.start();
System.out.println("睡眠中状态: " + sleeper.getState()); // TIMED_WAITING
3秒后自动恢复为 RUNNABLE
。
5. 任意状态 → TERMINATED
线程执行完 run()
方法或因未捕获异常退出时,进入终止状态。
Thread t = new Thread(() -> {
System.out.println("线程开始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
System.out.println("线程结束");
});
t.start();
t.join(); // 等待线程结束
System.out.println("最终状态: " + t.getState()); // TERMINATED
四、如何监控线程状态?
你可以通过 Thread.getState()
方法获取线程当前状态,这在调试多线程问题时非常有用。
Thread t = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE
t.sleep(100);
System.out.println(t.getState()); // TIMED_WAITING
// t.join() 会阻塞当前线程(main线程),直到线程t执行完毕
t.join();
System.out.println(t.getState()); // TERMINATED
五、常见误区
-
run()
vsstart()
直接调用run()
方法不会启动新线程,也不会改变线程状态,它只是普通方法调用,线程状态仍为NEW
。 -
BLOCKED
vsWAITING
BLOCKED
是被动等待锁。WAITING
是主动调用wait()
进入等待,需notify()
唤醒。
-
状态不是实时的
调用getState()
是瞬时快照,线程状态可能在两次调用之间已发生变化。
六、总结
状态 | 触发条件 | 变化条件 |
---|---|---|
NEW | 线程对象创建 | 调用 start() |
RUNNABLE | 正在运行或就绪 | —— |
BLOCKED | 等待进入 synchronized 块 | 获取到锁 |
WAITING | wait() 、join() 、park() | notify() /notifyAll() |
TIMED_WAITING | sleep() 、wait(timeout) | 超时或中断 |
TERMINATED | 线程执行结束 | 不可恢复 |
理解线程的生命周期,是排查死锁、活锁、性能瓶颈等问题的基础。在后续文章中,我们将深入探讨线程同步机制(如 synchronized
、ReentrantLock
)和线程间通信,这些机制正是通过控制线程状态转换来实现的。