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

为什么要有线程及其生命周期

壹、为什么要有线程

线程(Thread)是操作系统和编程语言中实现并发执行的核心机制,其存在的根本目的是提高程序执行效率和资源利用率。以下从计算机底层原理、程序执行模式及实际场景等维度,详细解析为什么需要线程:

一、从 "单任务" 到 "多任务":线程的诞生背景

早期计算机只能单任务执行(如 DOS 系统):一个程序运行时,其他程序必须等待其结束。这种模式存在严重缺陷:

  • 资源浪费:当程序进行 I/O 操作(如读取文件、网络请求)时,CPU 处于空闲状态(等待 I/O 完成),但无法处理其他任务。
  • 响应缓慢:若一个任务执行时间长(如复杂计算),整个系统会陷入 "假死",用户操作无响应。

为解决这些问题,操作系统引入了进程(Process)机制,实现多任务并发。但进程存在创建销毁成本高、资源占用大、切换开销大等问题(进程包含独立的内存空间、文件描述符等)。

线程的出现:线程是进程内的轻量级执行单元,多个线程共享进程的内存空间和资源,切换成本远低于进程。线程让多任务并发更高效,成为现代编程的基础。

二、线程的核心价值:提升效率与资源利用率

1. 充分利用 CPU 资源(减少空闲时间)

现代计算机几乎都配备多核 CPU,线程能让程序并行利用多个核心,同时在单核心下通过时间片轮转实现并发,减少 CPU 空闲。

  • 示例:一个程序包含两个任务:A(计算密集型,需大量 CPU)和 B(I/O 密集型,如读取文件)。
    • 单线程执行:A 执行时 CPU 忙碌,B 执行时 CPU 空闲(等待 I/O),总耗时 = A 耗时 + B 耗时。
    • 多线程执行:A 和 B 同时运行,CPU 在 A 执行时工作,B 等待 I/O 时 CPU 可处理 A(或其他任务),总耗时 ≈ max (A 耗时,B 耗时),效率显著提升。
2. 优化程序响应性(分离任务优先级)

线程可将程序的 "核心功能" 与 "辅助功能" 分离,避免耗时操作阻塞关键流程。

  • 典型场景:图形界面(GUI)程序中,若用单线程处理用户点击(关键)和文件下载(耗时),下载时界面会冻结(无法响应用户操作)。解决方案:用主线程处理用户交互,子线程处理文件下载,确保界面始终响应。
3. 简化并发编程模型

线程比进程更轻量,且共享进程资源,大幅降低了并发编程的复杂度:

  • 线程间通信无需复杂的进程间通信(IPC)机制(如管道、共享内存),可通过共享内存直接交互(需注意同步)。
  • 开发中可按功能拆分线程(如网络请求线程、数据处理线程、UI 线程),逻辑更清晰。
4. 适配现代硬件与应用场景
  • 多核 CPU:线程可直接映射到不同核心,实现真正的并行计算(如大数据处理、科学计算)。
  • I/O 密集型应用:服务器(如 Web 服务器、数据库)需同时处理 thousands 级请求,线程池(多线程)是支撑高并发的核心机制。
  • 实时系统:需快速响应外部事件(如传感器数据),线程的快速切换能力保证了实时性。

三、线程与相关概念的对比:为什么选择线程?

执行模式优势劣势适用场景
单线程简单,无并发问题无法利用多核,I/O 时 CPU 空闲简单工具、脚本程序
多进程隔离性好(一个崩溃不影响其他)资源占用大,切换成本高,通信复杂独立功能模块(如浏览器标签页)
多线程轻量,共享资源,切换快,通信简单需处理线程安全问题(如竞态条件)大多数并发场景(服务器、GUI 等)

四、线程的挑战与应对

线程并非完美,引入了新的问题:

  1. 线程安全:多个线程共享资源可能导致数据不一致(如竞态条件),需通过锁(synchronizedLock)、原子类等机制解决。
  2. 上下文切换:线程切换需保存 / 恢复状态(寄存器、程序计数器等),过度切换会消耗 CPU,需合理控制线程数量。
  3. 死锁 / 活锁:不当的同步逻辑可能导致线程无限等待,需通过规范锁顺序、设置超时等方式避免。

这些问题并非线程本身的缺陷,而是并发编程的固有挑战,通过合理设计(如使用线程池、并发容器)可有效规避。

五、总结

线程的存在是为了突破单任务执行的效率瓶颈,核心价值体现在:

  • 提高 CPU 利用率:让 CPU 在 I/O 等待时不空闲,充分利用多核资源。
  • 提升程序响应性:分离关键任务与耗时任务,避免阻塞。
  • 简化并发模型:比进程更轻量,共享资源便于通信。

从底层硬件(多核 CPU)到上层应用(Web 服务器、GUI 程序),线程是实现高效并发的基础,是现代编程语言和操作系统不可或缺的核心机制。

贰、线程的生命周期

在 Java 中,线程的生命周期包含6 种状态,这些状态定义在Thread.State枚举中,状态之间会根据操作发生转换。理解线程状态转换是掌握多线程编程的基础。

一、线程的 6 种状态

根据 Java 官方文档,线程的状态分为以下 6 种:

状态名称说明
NEW(新建)线程对象已创建,但未调用start()方法(尚未启动)。
RUNNABLE(可运行)线程已启动,正在 JVM 中运行,或等待 CPU 调度(包含传统 OS 中的 “运行” 和 “就绪” 状态)。
BLOCKED(阻塞)线程等待获取 synchronized 锁(因竞争锁而被阻塞)。
WAITING(无限等待)线程无限期等待其他线程的特定操作(如notify()),无超时时间。
TIMED_WAITING(计时等待)线程等待指定时间后自动唤醒(如sleep(1000)wait(1000))。
TERMINATED(终止)线程执行完毕(run()方法结束)或因异常终止。

二、状态转换流程图

[NEW] ↓ (调用start())
[RUNNABLE] ↓ (获取锁失败)              ↓ (释放锁后重新竞争)
[BLOCKED] --------------------→ [RUNNABLE]↑                            ↓ (调用wait()/join()等)|                        [WAITING]|                            ↓ (被notify()/中断)|                            → [RUNNABLE]|                            ↓ (调用wait(超时)/sleep(超时)等)|                        [TIMED_WAITING]|                            ↓ (超时/被notify()/中断)└----------------------------→ [RUNNABLE]↓ (run()执行完毕/异常)[TERMINATED]

三、详细转换说明

1. NEW → RUNNABLE
  • 触发条件:调用线程对象的start()方法(不能重复调用,否则抛IllegalThreadStateException)。
  • 示例:
    Thread thread = new Thread(() -> {});
    System.out.println(thread.getState()); // 输出:NEW
    thread.start();
    System.out.println(thread.getState()); // 可能输出:RUNNABLE(取决于调度)
    
2. RUNNABLE ↔ BLOCKED
  • RUNNABLE → BLOCKED:线程尝试获取synchronized锁时,若锁被其他线程持有,则进入BLOCKED状态。
  • BLOCKED → RUNNABLE:持有锁的线程释放锁后,当前线程竞争到锁,重新进入RUNNABLE
  • 示例:
    Object lock = new Object();// 线程1先获取锁
    Thread t1 = new Thread(() -> {synchronized (lock) {try { Thread.sleep(1000); } // 持有锁休眠catch (InterruptedException e) {}}
    });// 线程2后尝试获取锁,会进入BLOCKED
    Thread t2 = new Thread(() -> {synchronized (lock) { // 此时t1持有锁,t2进入BLOCKEDSystem.out.println("获取锁成功");}
    });t1.start();
    Thread.sleep(100); // 确保t1先执行
    t2.start();
    System.out.println(t2.getState()); // 输出:BLOCKED
    
3. RUNNABLE ↔ WAITING
  • RUNNABLE → WAITING

    • 调用object.wait()(必须在synchronized块中,释放锁并等待)。
    • 调用thread.join()(等待目标线程执行完毕)。
    • 调用LockSupport.park()(无参数版本,阻塞当前线程)。
  • WAITING → RUNNABLE

    • 其他线程调用object.notify()object.notifyAll()(唤醒后需重新竞争锁)。
    • join()的目标线程执行完毕。
    • 其他线程调用LockSupport.unpark(thread)
  • 示例(wait()/notify()):

    Object lock = new Object();
    Thread t = new Thread(() -> {synchronized (lock) {try {lock.wait(); // 释放锁,进入WAITING} catch (InterruptedException e) {}}
    });
    t.start();
    Thread.sleep(100);
    System.out.println(t.getState()); // 输出:WAITING// 其他线程唤醒
    new Thread(() -> {synchronized (lock) {lock.notify(); // 唤醒t线程}
    }).start();
    
4. RUNNABLE ↔ TIMED_WAITING
  • RUNNABLE → TIMED_WAITING

    • 调用Thread.sleep(long millis)(不释放锁,仅暂停执行)。
    • 调用object.wait(long timeout)(释放锁,等待指定时间)。
    • 调用thread.join(long millis)(限时等待目标线程)。
    • 调用LockSupport.parkNanos()LockSupport.parkUntil()(限时阻塞)。
  • TIMED_WAITING → RUNNABLE

    • 等待时间超时。
    • 其他线程调用notify()/notifyAll()(需重新竞争锁)。
    • 线程被中断(interrupt())。
  • 示例(sleep()):

    Thread t = new Thread(() -> {try {Thread.sleep(1000); // 进入TIMED_WAITING} catch (InterruptedException e) {}
    });
    t.start();
    Thread.sleep(100);
    System.out.println(t.getState()); // 输出:TIMED_WAITING
    
5. RUNNABLE → TERMINATED
  • 触发条件
    • 线程的run()方法正常执行完毕。
    • 线程执行中抛出未捕获的异常(导致run()方法终止)。
  • 示例:
    Thread t = new Thread(() -> {});
    t.start();
    Thread.sleep(100); // 等待线程执行完毕
    System.out.println(t.getState()); // 输出:TERMINATED
    

四、关键注意点

  1. RUNNABLE的特殊含义:Java 中的RUNNABLE包含两种情况:

    • 线程正在 CPU 上执行(运行中)。
    • 线程就绪,等待 CPU 调度(未运行但可立即执行)。这与操作系统层面的 “运行” 和 “就绪” 状态不同,是 Java 对线程状态的简化抽象。
  2. BLOCKEDWAITING的区别

    • BLOCKED:等待synchronized 锁(被动等待,无需主动唤醒)。
    • WAITING:等待其他线程的特定操作(如notify(),需主动唤醒)。
  3. 状态不可逆:线程进入TERMINATED后,无法再回到其他状态(即使调用start()也会报错)。

  4. sleep()wait()的状态差异

    • sleep():线程进入TIMED_WAITING不释放锁
    • wait():线程进入WAITINGTIMED_WAITING释放锁

总结

线程的生命周期是多线程调度的核心,6 种状态的转换反映了线程从创建到终止的完整过程:

  • NEW启动到RUNNABLE,再根据锁竞争、等待操作进入BLOCKED/WAITING/TIMED_WAITING,最终执行完毕进入TERMINATED

理解这些状态转换,有助于诊断线程死锁、阻塞等问题,写出更可靠的多线程程序。

http://www.dtcms.com/a/419614.html

相关文章:

  • 京东商品评论接口(jingdong.ware.comment.get)技术解析:数据拉取与情感分析优化
  • 县级门户网站建设运营成本广州昨天发生重大新闻
  • Java 调用高德地图Sig签名遇10007 INVALID_USER_SIGNATURE的解决之道
  • 代码式绘图工具--Mermaid
  • 网站营销方式有哪些内容wordpress客户端APP
  • 如何制作网站设计网站认证必须做么
  • 桐庐住房和城乡建设局网站wordpress+中文安装
  • linux网站备份杭州网络排名优化
  • 微网站怎么自己做线上外贸平台有哪些
  • 比价网站源码wordpress转域名收费
  • org域名做网站网站空间租用
  • 超越单向观看:视频直播点播视频会议平台EasyDSS如何赋能远程实时音视频互动场景?
  • 从打孔卡到SSD:数据存储300年的进化之路
  • 【音视频】pjsua2库:Endpoint、Call类接口详解
  • 网站策划书范文模板建设一个看电影的网站
  • 网站自适应框架文档下载免费网站
  • 内蒙古建设工程造价信息网官方网站wordpress网站乱码
  • AI + 咨询 | 2025年咨询行业 AI 实战报告
  • COMSOL介电弹性体数值仿真指南:预拉伸圆形DEA的稳态变形分析(II)
  • SpringBoot连接PgSQL
  • 计算机组成原理:外围设备概述
  • 从《智能世界2035》看软件研发组织变革与未来形态
  • 人事处网站开发文献综述深圳公司有哪些
  • NET网站开发程序员招聘抚顺做网站的公司
  • 数字化工艺设计与仿真:汽车制造业成本节约与效率跃升的转型之路
  • 汽车价格战全面熄火了?不卷价格该卷什么?
  • 甘肃网站域名申请公司网站被攻击打不开怎么办
  • centos7 离线安装mysql8 并实现主从架构
  • YOLO 11 图像分类推理 Web 服务
  • SMTPman,smtp服务商在邮件发送中的方案!