当前位置: 首页 > news >正文

【Java EE】深入理解 Java 线程的生命周期与状态转换

多线程编程在 Java 中是实现高效并发的核心技术之一。每个线程在其生命周期内会经历多个状态,这些状态反映了线程在特定时间点的行为与系统资源的使用情况。了解线程的状态及其转换机制,对于编写健壮的并发程序尤为重要。本文将深入探讨 Java 线程的六种状态、每种状态的含义、状态之间的转换条件以及线程状态在实际应用中的意义。

1. Java 线程的六种状态

根据 Java 虚拟机规范,线程状态可以被分为六种:

  1. 新建(New)
  2. 可运行(Runnable)
  3. 阻塞(Blocked)
  4. 等待(Waiting)
  5. 计时等待(Timed Waiting)
  6. 终止(Terminated)

这些状态为理解线程的生命周期提供了基本框架,帮助开发者掌握线程在不同场景下的行为。

2. 线程状态的含义详解

(1)新建(New)

线程处于新建状态时,它仅仅是一个被创建但尚未启动的对象。在这个状态下,线程对象已经被实例化,但线程还未被调度器纳入可运行线程的集合中。此时,线程还没有占用任何系统资源。

Thread thread = new Thread(() -> {System.out.println("线程处于新建状态");
});
// 线程尚未启动,处于新建状态

(2)可运行(Runnable)

可运行状态的线程表示它已经准备好执行代码,但并不一定立即执行。Java 中的可运行状态不仅包括真正占用 CPU 时间片的状态,也包括线程在操作系统层面处于等待资源调度的状态。线程在调用 start() 方法后,从新建状态转为可运行状态。

thread.start(); // 线程进入可运行状态

可运行状态的线程可以随时被操作系统调度执行,而调度的时机取决于系统的线程调度策略(例如时间片轮转、优先级调度等)。

(3)阻塞(Blocked)

阻塞状态是线程生命周期中的一个重要环节,通常发生在线程等待获取一个被其他线程持有的锁时。当线程试图进入同步代码块或者同步方法时,如果无法获得锁,它将进入阻塞状态。阻塞状态的线程无法继续执行,直到它成功获取锁为止。

public class Main {private static final Object lock = new Object();public static void main(String[] args) {Thread thread1 = new Thread(() -> {synchronized (lock) {System.out.println(Thread.currentThread().getName() + " 获得了锁");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2 = new Thread(() -> {synchronized (lock) {System.out.println(Thread.currentThread().getName() + " 获得了锁");}});thread1.start();thread2.start();}
}

在这个例子中,thread1 先获得锁并进入同步代码块,thread2 尝试获取相同的锁,但在 thread1 释放锁之前,thread2 会进入阻塞状态。

(4)等待(Waiting)

等待状态表示线程在等待某个条件的发生,只有当另一个线程显式地唤醒它时,线程才能从等待状态返回到可运行状态。典型的场景包括调用 Object.wait()Thread.join()LockSupport.park() 等方法。

public class Main {private static final Object lock = new Object();public static void main(String[] args) {Thread thread = new Thread(() -> {synchronized (lock) {try {System.out.println("线程进入等待状态");lock.wait(); // 线程进入等待状态System.out.println("线程恢复执行");} catch (InterruptedException e) {e.printStackTrace();}}});thread.start();try {Thread.sleep(1000);synchronized (lock) {lock.notify(); // 唤醒等待线程}} catch (InterruptedException e) {e.printStackTrace();}}
}

在此示例中,线程在 wait() 方法调用后进入等待状态,直到其他线程调用 notify() 方法将其唤醒。

(5)计时等待(Timed Waiting)

计时等待状态与等待状态类似,区别在于计时等待是有时间限制的。如果线程在指定的时间内没有被唤醒,它会自动从计时等待状态返回到可运行状态。计时等待通常用于控制线程的超时行为,比如通过 Thread.sleep()Object.wait(long timeout)Thread.join(long millis) 等方法来实现。

public class Main {public static void main(String[] args) {Thread thread = new Thread(() -> {try {System.out.println("线程进入计时等待状态");Thread.sleep(3000); // 线程进入计时等待状态System.out.println("线程恢复执行");} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}
}

在这个示例中,线程通过 Thread.sleep(3000) 进入计时等待状态,3秒后线程会自动恢复执行。

(6)终止(Terminated)

当线程的 run() 方法执行完成或由于未捕获的异常导致线程退出时,线程将进入终止状态。处于终止状态的线程不再具备可运行性,也不能被重新启动。在这种状态下,线程已经完成了其生命周期的所有阶段。

public class Main {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("线程运行完成");});thread.start();try {thread.join(); // 等待线程终止System.out.println("线程状态: " + thread.getState()); // 输出:TERMINATED} catch (InterruptedException e) {e.printStackTrace();}}
}

在该示例中,thread.join() 方法等待线程终止,终止后的线程状态为 TERMINATED

3. 线程状态之间的转换条件

线程的状态在其生命周期内根据特定条件进行转换。了解这些转换条件有助于我们更好地掌控线程的行为,并在开发过程中避免常见的并发问题。

  • 新建 -> 可运行:调用 start() 方法时,线程从新建状态进入可运行状态。
  • 可运行 -> 阻塞:线程试图获取一个被其他线程持有的锁时,会从可运行状态进入阻塞状态。
  • 阻塞 -> 可运行:当线程成功获取锁时,它会从阻塞状态返回到可运行状态。
  • 可运行 -> 等待:线程通过调用 Object.wait()Thread.join()LockSupport.park() 等方法进入等待状态。
  • 等待 -> 可运行:线程在被另一个线程显式唤醒(通过 Object.notify()notifyAll() 方法)后,从等待状态返回到可运行状态。
  • 可运行 -> 计时等待:线程通过调用具有超时参数的方法(如 Thread.sleep(long millis)Object.wait(long timeout) 等)进入计时等待状态。
  • 计时等待 -> 可运行:计时等待超时后,线程会自动从计时等待状态返回到可运行状态。
  • 可运行 -> 终止:当 run() 方法执行完成或线程因异常退出时,线程进入终止状态。

以下是线程状态转换的示意图:

        +--------------------+|                    |v                    |新建 (New) --> 可运行 (Runnable) <---> 计时等待 (Timed Waiting)|     ||     |阻塞 (Blocked)  |     |v     |等待 (Waiting)||终止 (Terminated)
4. 线程状态的实际应用与考虑

在实际开发中,线程状态的管理直接影响程序的正确性和性能。例如:

  • 死锁与阻塞状态:当多个线程相互等待对方释放资源时,可能会导致死锁问题,线程永远处于阻塞状态。合理设计锁的获取与释放机制可以避免死锁。

  • 等待/通知机制:在生产者-消费者模型中,生产者线程和消费者线程通过等待/通知机制实现高效的同步与通信。确保通知机制的正确性对于避免线程“假唤醒”非常关键。

  • 超时机制:通过计时等待,开发者可以为线程操作设置合理的超时时间,防止线程在等待过程中无限期地被挂起。超时机制在网络通信、文件 I/O 等场景中尤为重要。

结论

Java 线程的六种状态及其相互转换为我们提供了理解线程生命周期的基础。这些状态反映了线程在不同运行阶段的行为,也为我们管理线程提供了参考。在实际开发中,通过合理管理线程的状态,避免常见的并发问题,可以显著提升程序的可靠性和性能。

掌握线程的状态与转换规律,将使我们能够编写出更加健壮的并发程序,为复杂的多线程环境下提供稳定可靠的解决方案。出高效且安全的多线程程序。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Python 和 PyCharm 安装(傻瓜式)
  • C 06 编译4阶段
  • Linux 下 gdb 的使用
  • 10个企业网络安全建议,解决99%的网络安全问题
  • Clickhouse 二进制安装
  • 一个资深测试工程师面试一来就问我这些题目
  • 《黑神话·悟空》:国产3A大作背后是用什么语言开发的?
  • OpenGL实现3D游戏编程【连载5】——纹理坐标、纹理贴图
  • 链接 -- 动静态链接 --特点、区别、静态库安装下载
  • 基于huggingface peft进行qwen1.5-7b-chat训练/推理/服务发布
  • Java 入门指南:异常处理(Exception Handling)
  • 微信小程序记录(持续更新)
  • 嘉立创工程链接在哪里?
  • 一起搭WPF界面之View界面介绍一
  • PHP 过滤器
  • $translatePartialLoader加载失败及解决方式
  • 2017前端实习生面试总结
  • CSS中外联样式表代表的含义
  • download使用浅析
  • EOS是什么
  • JavaScript实现分页效果
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • Mac转Windows的拯救指南
  • Making An Indicator With Pure CSS
  • Mithril.js 入门介绍
  • Mysql5.6主从复制
  • MySQL的数据类型
  • PHP的Ev教程三(Periodic watcher)
  • Python打包系统简单入门
  • Python语法速览与机器学习开发环境搭建
  • springMvc学习笔记(2)
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 技术胖1-4季视频复习— (看视频笔记)
  • 入门级的git使用指北
  • 思考 CSS 架构
  • 通过git安装npm私有模块
  • 一个JAVA程序员成长之路分享
  • 原生Ajax
  • 1.Ext JS 建立web开发工程
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​人工智能之父图灵诞辰纪念日,一起来看最受读者欢迎的AI技术好书
  • ‌JavaScript 数据类型转换
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • $.ajax中的eval及dataType
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (2)MFC+openGL单文档框架glFrame
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (4)(4.6) Triducer
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (不用互三)AI绘画:科技赋能艺术的崭新时代
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (未解决)macOS matplotlib 中文是方框
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...