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

什么是虚拟线程?与普通线程的区别

引言:线程的演进与挑战

在传统的并发编程中,线程是一种非常重要的概念。我们使用线程来实现任务的并发执行,从而提高程序的执行效率。普通线程(如 Thread 类)是一种重量级的线程,每个线程都对应着操作系统内核中的一个线程,这意味着系统需要为每个线程分配独立的资源(如栈空间、内存等),从而可能导致性能瓶颈,尤其是在需要大量并发线程时。

为了克服这个问题,Java 在 JDK 21 中引入了 虚拟线程(Virtual Threads)。虚拟线程是轻量级的线程,它们由 Java 的 JVM 管理,并不直接依赖操作系统的线程调度机制,因此能够大规模地创建和管理大量并发任务。

在这篇文章中,我们将深入探讨虚拟线程的概念,并与传统的普通线程进行对比,帮助你理解虚拟线程的优势以及在实际开发中的应用场景。


一、普通线程的特点

普通线程(也称为操作系统线程)是操作系统直接管理的线程。每个线程都有自己的堆栈、程序计数器等资源。操作系统通过 线程调度器 来调度和管理这些线程,确保它们在多个处理器核心上执行。

普通线程的特点:
  • 操作系统管理:每个普通线程都由操作系统内核直接管理。
  • 重量级:创建和销毁线程的成本较高,因为操作系统需要为每个线程分配堆栈和其他资源。
  • 并发受限:由于线程创建开销大,操作系统通常会限制同时运行的线程数量,导致高并发时出现资源竞争和上下文切换的开销。
  • 上下文切换:操作系统通过上下文切换来切换不同线程的执行状态,这个过程是有成本的,特别是在大量线程的情况下。

二、虚拟线程的概念

虚拟线程是 JDK 21 引入的一项新特性,它为 Java 提供了一种轻量级的并发模型。虚拟线程并不依赖操作系统的线程调度,而是由 JVM 负责调度和管理。每个虚拟线程的栈大小和执行成本都比普通线程小得多,因此可以大规模地创建虚拟线程,从而实现高并发。

虚拟线程的特点:
  • JVM管理:虚拟线程由 JVM 管理,而非操作系统。JVM 会将多个虚拟线程映射到少量的操作系统线程上。
  • 轻量级:虚拟线程的栈空间小,创建和销毁的成本也远低于操作系统线程。
  • 大规模并发:可以创建成千上万甚至更多的虚拟线程,而不会导致性能瓶颈。
  • 调度效率高:由于 JVM 管理线程调度,虚拟线程的上下文切换非常高效,几乎不需要操作系统的参与。
  • 适合 IO 密集型任务:虚拟线程特别适合处理大量的 IO 密集型任务,因为它们的生命周期成本低,能够快速切换。

三、虚拟线程和普通线程的区别

虚拟线程和普通线程有很多显著的区别,以下是几个关键点:

特性普通线程虚拟线程
管理者操作系统(内核线程)JVM(用户空间线程)
创建成本较高,需要为每个线程分配栈和资源较低,创建虚拟线程的成本非常小
栈空间每个线程都有独立的栈空间,通常为1MB左右轻量级的栈空间,通常为几KB到几十KB
线程调度操作系统负责调度JVM负责调度,依赖于 JVM 的线程池
上下文切换操作系统进行上下文切换,较慢JVM 高效的上下文切换,几乎无成本
适用场景适用于 CPU 密集型任务适用于 IO 密集型任务
线程数限制受操作系统限制,通常在几十到几百个之间可以创建成千上万的虚拟线程
性能开销线程创建和销毁成本较高,内存占用大线程创建和销毁成本低,内存占用少

四、虚拟线程的优势

  1. 大规模并发处理:

    • 由于虚拟线程的栈空间较小,且创建和销毁成本低,理论上可以创建数百万个虚拟线程。这使得它在处理高并发任务时,比传统的线程更具优势。
    • 例如,处理大量独立的 IO 操作(如 HTTP 请求处理、数据库查询等)时,虚拟线程能够提供极高的性能。
  2. 低内存占用:

    • 虚拟线程的内存开销比普通线程要小得多。每个虚拟线程的栈大小仅为普通线程的几分之一,因此可以在有限的内存空间中创建更多的线程。
  3. 高效的上下文切换:

    • 传统线程的上下文切换由操作系统负责,通常较为昂贵。而虚拟线程的上下文切换是由 JVM 管理的,效率极高,几乎没有开销。
  4. 适应异步编程模型:

    • 虚拟线程非常适合处理异步任务和 IO 密集型操作,能够以同步的方式编写异步代码。例如,在处理大量 HTTP 请求时,可以通过虚拟线程编写出更简洁、易于理解的同步代码,而不需要传统的回调或 Future。

五、如何使用虚拟线程

JDK 21 引入了一个名为 java.util.concurrent.VirtualThread 的新 API,它使得虚拟线程的使用变得非常简单。下面是如何在 Java 中使用虚拟线程的一个基本示例:

public class VirtualThreadExample {

    public static void main(String[] args) {
        // 创建一个虚拟线程并启动
        Thread virtualThread = Thread.ofVirtual().start(() -> {
            System.out.println("Hello from virtual thread!");
        });

        // 等待虚拟线程执行完成
        virtualThread.join();
    }
}

在上面的代码中,我们使用 Thread.ofVirtual() 来创建虚拟线程,虚拟线程启动后会执行指定的任务。与传统线程相比,虚拟线程的使用方式几乎没有变化。


六、虚拟线程的应用场景

虚拟线程非常适合以下场景:

  • 高并发的 IO 密集型任务:例如 Web 服务器、网络爬虫、文件系统操作等。虚拟线程能够处理大量的并发请求,而不需要过多的内存开销。
  • 异步编程模型:通过虚拟线程,可以用同步的方式处理异步任务,使得代码更加简洁易懂。
  • 任务调度系统:虚拟线程也非常适合用于任务调度系统,可以大规模地调度并发任务。

七、总结:虚拟线程的未来

虚拟线程的引入为 Java 并发编程提供了全新的选择,使得我们能够更加高效地处理大规模并发任务。它的轻量级、高效性和低内存占用,使得虚拟线程成为处理 IO 密集型任务的理想选择。

在未来,随着 Java 生态的进一步发展,我们预计虚拟线程会在更多的领域得到应用,特别是在大规模并发处理和高性能计算领域。

如果你目前还在使用传统的线程池来处理并发任务,虚拟线程无疑是一个值得尝试的技术,它可以大大简化你的代码并提升性能。

推荐阅读文章

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)

  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系

  • HTTP、HTTPS、Cookie 和 Session 之间的关系

  • 什么是 Cookie?简单介绍与使用方法

  • 什么是 Session?如何应用?

  • 使用 Spring 框架构建 MVC 应用程序:初学者教程

  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

  • 如何理解应用 Java 多线程与并发编程?

  • 把握Java泛型的艺术:协变、逆变与不可变性一网打尽

  • Java Spring 中常用的 @PostConstruct 注解使用总结

  • 如何理解线程安全这个概念?

  • 理解 Java 桥接方法

  • Spring 整合嵌入式 Tomcat 容器

  • Tomcat 如何加载 SpringMVC 组件

  • “在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”

  • “避免序列化灾难:掌握实现 Serializable 的真相!(二)”

  • 如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)

  • 解密 Redis:如何通过 IO 多路复用征服高并发挑战!

  • 线程 vs 虚拟线程:深入理解及区别

  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别

  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!

  • “打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”

  • Java 中消除 If-else 技巧总结

  • 线程池的核心参数配置(仅供参考)

  • 【人工智能】聊聊Transformer,深度学习的一股清流(13)

  • Java 枚举的几个常用技巧,你可以试着用用

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)

  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系

  • HTTP、HTTPS、Cookie 和 Session 之间的关系

  • 使用 Spring 框架构建 MVC 应用程序:初学者教程

  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

  • Java Spring 中常用的 @PostConstruct 注解使用总结

  • 线程 vs 虚拟线程:深入理解及区别

  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别

  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!

  • 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)

  • 为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)

相关文章:

  • 做网站建设公司赚钱吗营销型网站建设专家
  • 武汉营销网站建设公司手机如何创建网站
  • 下载手机app客户端下载安装如何结合搜索检索与seo推广
  • 做sns网站需要什么成都seo顾问
  • 网站开发 java c php百度目前的推广方法
  • 做网站构架b站引流推广网站
  • python基础语法14-多线程与多进程
  • 校园智能硬件国产化的现状与意义
  • 使用层次聚类算法对wine数据集进行聚类分析
  • Flink的数据流图中的数据通道 StreamEdge 详解
  • 如何保持自己在职场的核心竞争力
  • Python贝叶斯回归、强化学习分析医疗健康数据拟合截断删失数据与参数估计3实例
  • icoding题解排序
  • NO.87十六届蓝桥杯备战|动态规划-完全背包|疯狂的采药|Buying Hay|纪念品(C++)
  • x265 编码器中运动搜索 ME 方法对比实验
  • C++基础精讲-03
  • 苍穹外卖总结
  • 【Web API系列】WebSocketStream API 深度实践:构建高吞吐量实时应用的流式通信方案
  • 23种设计模式生活化场景,帮助理解
  • 洛谷刷题Day1——P1706+P1157+P2089+P3654
  • 要查看 FAISS 使用的 OpenMP 版本,需根据安装方式和系统环境采用不同方法。以下是具体步骤和原理分析:
  • [设计模式]发布订阅者模式解耦业务和UI(以Axios拦截器处理响应状态为例)
  • Spring Boot 自动加载流程详解
  • 8.3.5 ToolStripContainer(工具栏容器)控件
  • 线代第四课:行列式的性质
  • 电子元件浸入式冷却