快速入门多线程(一):线程生命周期详解(附流程图详解)
线程的生命周期是多线程编程的核心概念之一,理解它能帮助你更好地控制线程行为,避免常见的并发问题。本文结合流程图、生活化比喻和代码示例来详细解析。
一、线程生命周期全景图
二、六大状态详解(JVM视角)
1. 新建状态(New)
- 定义:线程对象已创建(
new Thread()
),但尚未调用start()
方法。 - 比喻:
就像婴儿刚出生,还未开始活动。 - 代码:
Thread t = new Thread(() -> System.out.println("线程运行中")); // 新建状态
2. 就绪状态(Runnable)
- 定义:线程已启动(调用
start()
),正在JVM中运行,但可能在等待操作系统分配CPU时间片。 - 比喻:
运动员站在起跑线,已准备好但需等待裁判鸣枪。 - 关键点:
- 线程进入就绪队列,由操作系统调度器决定何时执行。
start()
方法只能调用一次,否则抛出IllegalThreadStateException
。
- 代码:
t.start(); // 进入就绪状态,等待CPU调度
3. 运行状态(Running)
- 定义:线程获得CPU时间片,正在执行
run()
方法中的代码。 - 比喻:
运动员听到枪声开始奔跑。 - 关键点:
- 线程可能因时间片用完或主动放弃CPU而回到就绪状态。
- 多核CPU系统中,多个线程可同时处于运行状态。
4. 阻塞状态(Blocked)
- 定义:线程等待获取锁(如
synchronized
块)时的状态。 - 比喻:
多人排队使用公共厕所,当前厕所有人占用,需等待。 - 代码示例:
public class BlockedDemo {private static final Object LOCK = new Object();public static void main(String[] args) {Thread t1 = new Thread(() -> {synchronized (LOCK) {try { Thread.sleep(1000); } catch (InterruptedException e) {}}});Thread t2 = new Thread(() -> {synchronized (LOCK) { // 若t1持有锁,t2进入Blocked状态System.out.println("t2获取到锁");}});t1.start();t2.start();} }
5. 等待状态(Waiting)
- 定义:线程调用
wait()
、join()
或LockSupport.park()
后进入的状态,需显式唤醒。 - 比喻:
顾客在餐厅等待服务员通知座位就绪。 - 代码示例:
public class WaitingDemo {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {synchronized (WaitingDemo.class) {try {WaitingDemo.class.wait(); // 进入Waiting状态} catch (InterruptedException e) {}}});t.start();Thread.sleep(100);System.out.println("t的状态: " + t.getState()); // 输出WAITINGsynchronized (WaitingDemo.class) {WaitingDemo.class.notify(); // 唤醒线程}} }
6. 超时等待状态(Timed Waiting)
- 定义:线程调用
Thread.sleep()
、wait(timeout)
、join(timeout)
等带超时参数的方法后进入的状态。 - 比喻:
顾客设定了等待餐厅座位的最长时间,超时后自行离开。 - 代码示例:
public class TimedWaitingDemo {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {try {Thread.sleep(2000); // 进入Timed Waiting状态2秒} catch (InterruptedException e) {}});t.start();Thread.sleep(100);System.out.println("t的状态: " + t.getState()); // 输出TIMED_WAITING} }
7. 死亡状态(Terminated)
- 定义:线程执行完毕(
run()
方法正常返回)或因未捕获的异常终止。 - 比喻:
运动员完成比赛或中途退赛。 - 关键点:
- 死亡状态的线程无法重新启动,需创建新线程。
- 代码示例:
Thread t = new Thread(() -> System.out.println("线程运行中")); t.start(); Thread.sleep(100); // 等待线程执行完毕 System.out.println("t的状态: " + t.getState()); // 输出TERMINATED
三、状态转换核心方法
方法 | 作用 | 导致的状态转换 |
---|---|---|
start() | 启动线程 | 新建 → 就绪 |
sleep(long) | 线程休眠指定时间 | 运行 → 超时等待 → 就绪 |
wait() | 释放锁并等待通知 | 运行 → 等待 → 就绪 |
notify()/notifyAll() | 唤醒等待的线程 | 无(需配合wait() 使用) |
join() | 等待线程结束 | 运行 → 等待 → 就绪 |
synchronized | 获取对象锁 | 运行 → 阻塞 → 就绪 |
LockSupport.park() | 暂停当前线程 | 运行 → 等待 → 就绪 |
LockSupport.parkNanos(long) | 暂停指定时间 | 运行 → 超时等待 → 就绪 |
四、线程生命周期完整示例
public class ThreadLifeCycleDemo {public static void main(String[] args) throws InterruptedException {// 1. 新建状态Thread t = new Thread(() -> {System.out.println("线程进入运行状态");// 3. 超时等待状态(sleep)try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 4. 阻塞状态(等待锁)synchronized (ThreadLifeCycleDemo.class) {try {// 5. 等待状态(wait)ThreadLifeCycleDemo.class.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程即将结束");});// 2. 就绪状态t.start();System.out.println("线程启动后状态: " + t.getState()); // RUNNABLE// 主线程休眠,让子线程有机会执行Thread.sleep(200);System.out.println("子线程sleep时状态: " + t.getState()); // TIMED_WAITING// 等待子线程进入wait状态Thread.sleep(1200);System.out.println("子线程wait时状态: " + t.getState()); // WAITING// 唤醒子线程synchronized (ThreadLifeCycleDemo.class) {ThreadLifeCycleDemo.class.notify();}// 等待子线程执行完毕t.join();System.out.println("子线程结束后状态: " + t.getState()); // TERMINATED}
}
五、常见问题与注意事项
-
线程复用问题:
死亡状态的线程无法重启,需避免重复调用start()
。 -
阻塞与忙等待:
阻塞状态(如sleep()
、wait()
)会释放CPU资源,而忙等待(如while(true)
)会持续占用CPU。 -
线程安全:
多线程在状态转换过程中可能出现竞态条件,需使用synchronized
、Lock
或原子类保护共享资源。 -
中断机制:
使用interrupt()
优雅地终止线程,而非强制终止(stop()
已被弃用)。Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {// 执行任务} }); t.start(); t.interrupt(); // 中断线程
六、总结
线程的生命周期就像一场精心编排的舞蹈:
- 新建:舞者站到舞台一侧(创建线程对象)。
- 就绪:等待音乐响起(等待CPU调度)。
- 运行:尽情舞蹈(执行代码)。
- 阻塞:暂停动作等待道具(等待资源)。
- 等待:定格姿势等待提示(等待通知)。
- 超时等待:按预定时间暂停(超时自动恢复)。
- 死亡:舞蹈结束谢幕(执行完成)。
理解每个状态的转换条件和对应的方法,方便我们能更精准地控制线程行为,编写出高效、稳定的并发程序。