Java 线程生命周期与状态转换

一、前言

在上一篇文章中,我们学习了 Java 中创建线程的 5 种方式,掌握了线程的基础创建与启动逻辑。但线程从创建到销毁并非一成不变,而是会在不同状态之间转换。理解线程的生命周期和状态转换,是解决多线程并发问题的关键。

本文将从理论到实践,深入解析 Java 线程的 6 种状态,演示状态转换的核心路径,并通过工具分析线程状态,帮助大家彻底掌握这一核心知识点。

 

二、Java 线程的 6 种状态

Java 线程的状态被明确枚举在 Thread.State 中,共包含 6 种状态 ,任何时刻一个线程只能处于其中一种状态。

状态名称

核心含义

进入条件

退出条件

NEW

(新建)

线程对象已创建,但未调用start()方法

创建 Thread 实例后,未执行 start()

调用 start() 方法

RUNNABLE

(可运行)

线程正在 CPU 上执行,或处于就绪状态等待 CPU 调度

调用 start() 方法;从其他阻塞 / 等待状态唤醒

线程执行完毕;进入阻塞 / 等待状态

BLOCKED

(阻塞)

线程因竞争锁失败而被阻塞

线程尝试进入 synchronized 代码块 / 方法,但锁被其他线程持有

成功获取到锁

WAITING

(等待)

线程无时间限制等待,需其他线程主动唤醒

调用 Object.wait()(无参)、Thread.join()(无参)、LockSupport.park()

其他线程调用 Object.notify()/notifyAll()、被 join 的线程执行完毕、调用 LockSupport.unpark()

TIMED_WAITING

(超时等待)

线程有时间限制等待,超时后自动唤醒

调用 Thread.sleep(long)、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos()/parkUntil()

超时自动唤醒;其他线程提前唤醒

TERMINATED

(终止)

线程执行完毕或异常退出

run() 方法执行完成;线程执行过程中抛出未捕获异常

无(线程生命周期结束)

 

三、线程状态转换流程图

 

 

 

四、线程状态转换

线程状态转换有固定的核心路径,我们通过代码案例逐一演示关键转换场景,直观观察状态变化。

前置说明

  • 所有案例中,使用 Thread.getState() 方法获取线程当前状态。
  • 为了精准观察状态,使用 Thread.sleep() 控制线程执行节奏,避免因 CPU 调度过快导致状态无法捕捉。

场景 1:NEW → RUNNABLE → TERMINATED(基础路径)

核心逻辑 :线程创建后处于 NEW 状态,调用 start() 进入 RUNNABLE ,执行完 run() 后进入 TERMINATED 。

public class ThreadStateDemo1 {     public static void main(String[] args) throws InterruptedException {         // 1. 创建线程,此时状态为 NEW         Thread thread = new Thread(() -> {             System.out.println("线程执行中,当前状态:" + Thread.currentThread().getState());             // 模拟任务执行             try {                 Thread.sleep(100);             } catch (InterruptedException e) {                 e.printStackTrace();             }         }, "基础状态线程");         System.out.println("调用start()前,线程状态:" + thread.getState()); // NEW         // 2. 启动线程,进入 RUNNABLE 状态         thread.start();         Thread.sleep(50); // 等待线程进入执行状态         System.out.println("线程执行中,主线程观察到的状态:" + thread.getState()); // RUNNABLE         // 3. 等待线程执行完毕         thread.join();         System.out.println("线程执行完毕,当前状态:" + thread.getState()); // TERMINATED     } }

运行结果

调用start()前,线程状态:NEW 线程执行中,当前状态:RUNNABLE 线程执行中,主线程观察到的状态:RUNNABLE 线程执行完毕,当前状态:TERMINATED

 

场景 2:RUNNABLE → BLOCKED → RUNNABLE(锁竞争导致阻塞)

核心逻辑 :线程尝试获取 synchronized 锁失败时,进入 BLOCKED 状态;成功获取锁后,回到 RUNNABLE 。

public class ThreadStateDemo2 {     // 定义一个锁对象     private static final Object LOCK = new Object();     public static void main(String[] args) throws InterruptedException {         // 线程1:先获取锁,持有锁1秒         Thread thread1 = new Thread(() -> {             synchronized (LOCK) {                 try {                     System.out.println(Thread.currentThread().getName() + " 获取到锁,状态:RUNNABLE");                     Thread.sleep(1000); // 持有锁,让线程2阻塞                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }, "线程1");         // 线程2:后获取锁,会进入 BLOCKED 状态         Thread thread2 = new Thread(() -> {             synchronized (LOCK) {                 System.out.println(Thread.currentThread().getName() + " 成功获取锁,状态回到 RUNNABLE");             }         }, "线程2");         // 启动线程1和线程2         thread1.start();         Thread.sleep(100); // 确保线程1先获取锁         thread2.start();         // 观察线程2的状态         Thread.sleep(200);         System.out.println(thread2.getName() + " 等待锁,状态:" + thread2.getState()); // BLOCKED         // 等待线程1执行完毕,线程2获取锁         thread1.join();         Thread.sleep(200);         System.out.println(thread2.getName() + " 执行中,状态:" + thread2.getState()); // RUNNABLE     } }

运行结果

线程1 获取到锁,状态:RUNNABLE 线程2 等待锁,状态:BLOCKED 线程2 成功获取锁,状态回到 RUNNABLE 线程2 执行中,状态:RUNNABLE

 

场景 3:RUNNABLE → WAITING → RUNNABLE(无参 wait () 唤醒)

核心逻辑 :线程调用 Object.wait() (无参)后,释放锁并进入 WAITING 状态;需其他线程调用 notify() / notifyAll() 唤醒,重新竞争锁后回到 RUNNABLE 。

public class ThreadStateDemo3 {     private static final Object LOCK = new Object();     private static Thread waitThread; // 等待线程     public static void main(String[] args) throws InterruptedException {         // 等待线程:调用wait()进入WAITING状态         waitThread = new Thread(() -> {             synchronized (LOCK) {                 try {                     System.out.println(Thread.currentThread().getName() + " 调用wait(),进入WAITING状态");                     LOCK.wait(); // 无参wait,释放锁                     System.out.println(Thread.currentThread().getName() + " 被唤醒,状态回到RUNNABLE");                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }, "等待线程");         // 唤醒线程:调用notify()唤醒等待线程         Thread notifyThread = new Thread(() -> {             synchronized (LOCK) {                 System.out.println(Thread.currentThread().getName() + " 获取锁,准备唤醒等待线程");                 LOCK.notify(); // 唤醒等待线程                 try {                     Thread.sleep(500); // 持有锁,让等待线程先处于WAITING→BLOCKED                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }         }, "唤醒线程");         waitThread.start();         Thread.sleep(200);         System.out.println(waitThread.getName() + " 当前状态:" + waitThread.getState()); // WAITING         notifyThread.start();         notifyThread.join();         Thread.sleep(200);         System.out.println(waitThread.getName() + " 最终状态:" + waitThread.getState()); // RUNNABLE/TERMINATED     } }

运行结果

等待线程 调用wait(),进入WAITING状态 等待线程 当前状态:WAITING 唤醒线程 获取锁,准备唤醒等待线程 等待线程 被唤醒,状态回到RUNNABLE 等待线程 最终状态:TERMINATED

 

场景 4:RUNNABLE → TIMED_WAITING → RUNNABLE(sleep () 超时唤醒)

核心逻辑 :线程调用 Thread.sleep(long) 后,进入 TIMED_WAITING 状态;超时后自动唤醒,回到 RUNNABLE 状态( sleep () 不会释放锁 )。

public class ThreadStateDemo4 {     public static void main(String[] args) throws InterruptedException {         Thread sleepThread = new Thread(() -> {             try {                 System.out.println(Thread.currentThread().getName() + " 调用sleep(1000),进入TIMED_WAITING状态");                 Thread.sleep(1000); // 超时等待1秒             } catch (InterruptedException e) {                 e.printStackTrace();             }             System.out.println(Thread.currentThread().getName() + " 超时唤醒,状态回到RUNNABLE");         }, "睡眠线程");         sleepThread.start();         Thread.sleep(200);         System.out.println(sleepThread.getName() + " 当前状态:" + sleepThread.getState()); // TIMED_WAITING         sleepThread.join();         System.out.println(sleepThread.getName() + " 执行完毕,状态:" + sleepThread.getState()); // TERMINATED     } }

运行结果

睡眠线程 调用sleep(1000),进入TIMED_WAITING状态 睡眠线程 当前状态:TIMED_WAITING 睡眠线程 超时唤醒,状态回到RUNNABLE 睡眠线程 执行完毕,状态:TERMINATED

 

五、使用 jstack 命令分析线程状态

在实际开发中,我们无法通过代码实时观察线上线程的状态,此时可以使用 JDK 自带的 jstack 命令,查看线程的状态和调用栈,定位线程阻塞、死锁等问题。

1、操作步骤

1. 获取进程 ID:使用 jps 命令,查看 Java 进程的 PID。

jps # 输出示例:1234 ThreadStateDemo2 (1234为PID)

2. 导出线程快照:使用 jstack PID > thread_dump.txt 命令,将线程快照导出到文件。

jstack 1234 > thread_dump.txt

3. 分析线程状态:打开 thread_dump.txt 文件,搜索线程名称,查看线程状态。

 

2、实战案例(分析 BLOCKED 状态线程)

在 ThreadStateDemo2 运行时,导出线程快照,会看到类似如下内容:

"线程2" #13 prio=5 os_prio=31 tid=0x00007f8b1282e000 nid=0x5003 waiting for monitor entry [0x0000700001a6f000]    java.lang.Thread.State: BLOCKED (on object monitor)         at ThreadStateDemo2.lambda$main$1(ThreadStateDemo2.java:18)         - waiting to lock <0x000000076ab81810> (a java.lang.Object)         - locked <0x000000076ab81810> (a java.lang.Object)
  • java.lang.Thread.State: BLOCKED (on object monitor):明确线程处于 BLOCKED 状态。
  • waiting to lock <0x000000076ab81810>:表示线程正在等待获取该地址的锁。

 

六、常见误区辨析

  1. RUNNABLE 状态 ≠ 正在执行:RUNNABLE 包含 “就绪” 和 “运行中” 两种情况,只有获取到 CPU 时间片的线程才会真正执行。
  2. sleep () 和 wait () 的区别: sleep() 属于 Thread 类,不会释放锁,超时自动唤醒; wait() 属于 Object 类,会释放锁,需要其他线程唤醒。
  3. BLOCKED 和 WAITING 的区别: BLOCKED 是因竞争锁阻塞, WAITING 是因调用 wait() / join() 等方法主动等待。

 

六、总结

本文详细讲解了 Java 线程的 6 种状态,通过 4 个代码案例演示了状态转换的核心路径,并介绍了使用 jstack 命令分析线程状态的实战方法。理解线程的生命周期和状态转换,是后续学习线程同步、线程通信的基础。

下一篇文章,我们将深入分析并发编程的三大核心特性(原子性、可见性、有序性),并讲解volatile关键字的原理与使用场景。

Read more

Git 远程操作全攻略:从基础到实战

Git 远程操作全攻略:从基础到实战

🌈 个人主页:Zfox_ 🔥 系列专栏:Git 企业级应用 目录 * 一:🔥 理解分布式版本控制系统 * 二:🔥 远程仓库 * 🦋 新建远程仓库 * 🦋 克隆远程仓库 * 🦋 向远程仓库推送 * 🦋 拉取远程仓库 * 三:🔥 配置Git * 🦋 忽略特殊⽂件 * 🦋 给命令配置别名 * 四:🔥 标签管理 * 🦋 理解标签 * 🦋 创建标签 * 🦋 操作标签 * 五:🔥 多⼈协作 * 🦋 多⼈协作⼀ * 🦋 多⼈协作⼆ * 🎀 远程分⽀删除后,本地gitbranch-a依然能看到的解决办法 * 六:🔥 共勉 一:🔥 理解分布式版本控制系统 🦈 我们⽬前所说的所有内容(⼯作区,暂存区,版本库 等等),都是在本地!也就是在你的笔记本或者计算机上。⽽我们的Git其实是分布式版本控制系统!什么意思呢? 可以简单理解为,我们每个⼈

By Ne0inhk

开源又实用!CAM++系统为何值得你立刻尝试

开源又实用!CAM++系统为何值得你立刻尝试 1. 这不是另一个语音识别工具,而是一个真正能落地的说话人验证方案 你有没有遇到过这样的场景:需要确认一段录音是不是某位同事说的?想快速判断客服通话中两个声音是否来自同一人?或者在安防系统里,需要从一段监控音频中验证说话人身份?市面上很多语音识别工具只告诉你“说了什么”,但CAM++解决的是更关键的问题——“谁说的”。 CAM++不是语音转文字(ASR),也不是语音合成(TTS),它专注一个被长期低估却极其重要的能力:说话人验证(Speaker Verification)。简单说,它不关心内容,只认声音本身。就像指纹或虹膜识别一样,它把人的声纹变成一串可计算、可比对的数字特征。 更难得的是,这个系统完全开源、开箱即用、中文优化、部署极简。不需要GPU服务器,一台普通开发机就能跑;不需要写代码,点点鼠标就能完成专业级声纹分析;不需要调参经验,预设阈值开箱即准。它不像学术模型那样只停留在论文里,也不像商业API那样藏着高昂费用和隐私风险——它就安静地运行在你的本地机器上,数据不出门,结果自己掌控。 如果你正在寻找一个真正能放进工作流

By Ne0inhk
【工创赛2025-智能物流搬运塔吊方案开源(2分15秒)】西安理工大学工程训练中心

【工创赛2025-智能物流搬运塔吊方案开源(2分15秒)】西安理工大学工程训练中心

一、前言        时光荏苒,岁月如梭。三年的本科竞赛生涯随着工训赛的结束告一段落。竞赛路途中,受到了诸多大佬的帮助和鼓励。为了将这份开源精神传递下去,本团队全体成员一致决定无偿开源本项目机械设计图纸、PCB设计、电控代码、视觉代码及镜像文件、参赛文档以及其他有关设计资料。        请注意,本项目开源文件完全免费,内容遵循CC 4.0 BY-NC-SA版权协议,转载请给出适当的署名,不可用作商业用途,严禁倒卖,若广大网友发现以上行为,请第一时间与我取得联系。        在此,由衷感谢西安理工大学工程训练中心的各位老师对我们竞赛项目的悉心指导与鼎力支持。         这里放一张二代小车同堂的照片作为纪念 二、关于开源项目        运行视频:[开源]2025工训赛智能物流搬运,初赛第八,2分26秒_哔哩哔哩_bilibili        本项目参与了2025年中国大学生工程实践与创新能力大赛全国总决赛,初赛成绩仅1个二环,其余均为一环,总时间2分26秒。决赛由于准备不足以及现场不可预料的因素,成绩不算理想,最后总成绩为全国特等奖。

By Ne0inhk