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

Java 提供了两种线程机制:普通线程(平台线程)和 虚拟线程。普通线程是 Java 中经典的并发处理方式,而虚拟线程是随着 Java 21 引入的新特性,旨在提升并发性能和开发体验。本文将详细探讨它们的区别,并帮助你理解如何在实际开发中利用这些机制。

一、什么是线程?

在计算机科学中,线程是程序执行的最小单位。每个线程有自己独立的栈和程序计数器,能够独立执行代码。Java 提供了多线程机制,使得多个线程可以并发地执行任务,提高程序的响应速度和处理能力。

普通线程,也称为平台线程,是直接映射到操作系统的内核线程。每个线程都有自己独立的资源(栈、寄存器等),并且受限于操作系统对线程的管理与调度。

二、普通线程的特点
  1. 重量级线程:普通线程直接与操作系统线程绑定,因此是“重量级”线程。创建、销毁线程需要操作系统分配资源,导致其成本较高。

  2. 并发调度:操作系统负责对所有普通线程进行调度,这意味着线程数的增加会给系统带来额外的上下文切换开销,导致性能下降。

  3. 阻塞问题:由于普通线程直接与操作系统内核线程绑定,任何阻塞操作(如 I/O 操作)会导致线程被挂起,从而降低系统整体的吞吐量。

  4. 线程池的使用:由于普通线程的高创建成本,开发者通常会使用线程池来管理线程,以避免频繁创建和销毁线程带来的性能开销。

    ExecutorService executor = Executors.newFixedThreadPool(10);
    for (int i = 0; i < 100; i++) {executor.submit(() -> {System.out.println("Running task");});
    }
    executor.shutdown();
    
三、虚拟线程的引入

随着并发应用需求的增长,Java 在 JDK 21 中引入了虚拟线程(Virtual Threads),也称为轻量级线程。虚拟线程旨在解决普通线程存在的开销高、并发受限的问题,从而简化高并发场景下的编程模式。

虚拟线程本质上是一个用户级线程,独立于操作系统的线程调度。虚拟线程由 Java 虚拟机(JVM)管理,创建和销毁的开销非常小,能够并行执行数百万个线程。

四、虚拟线程的特点
  1. 轻量级线程:虚拟线程是“轻量级”的,创建、销毁和切换的成本非常低,允许开发者轻松创建数百万个虚拟线程。相比之下,普通线程的数量受限于操作系统资源。

  2. 非阻塞模型:虚拟线程能够非阻塞地处理任务,即使线程被阻塞,JVM 也可以在另一个内核线程上继续调度其他虚拟线程,从而最大化 CPU 利用率。

    Thread.startVirtualThread(() -> {// 虚拟线程中的任务System.out.println("Running in a virtual thread");
    });
    
  3. 资源节约:由于虚拟线程使用的资源较少,它们可以显著减少内存和 CPU 的消耗,特别是在高并发的 I/O 密集型任务中表现出色。

  4. 可伸缩性:虚拟线程非常适合处理大量短时间运行的任务,这使得它们在高并发系统中具有更好的扩展性和可伸缩性。

  5. 自动调度:JVM 会自动调度虚拟线程到内核线程上,开发者不再需要手动配置线程池,这极大地简化了多线程编程。

    虚拟线程的出现将 Java 线程模型的设计思路从原来的“少而精”变为“多而轻”,使得每个任务都可以拥有自己的独立线程,而无需考虑线程资源的限制。

五、普通线程与虚拟线程的区别
特性普通线程虚拟线程
创建成本高,需要操作系统分配内核资源低,由 JVM 轻量管理
数量限制受限于操作系统资源可以创建数百万个虚拟线程
阻塞行为阻塞会占用操作系统线程阻塞时,JVM 自动调度其他任务
上下文切换成本高,涉及内核态和用户态切换低,全部在用户态完成
适用场景适合长时间运行的任务适合高并发的短期任务
资源消耗需要大量的内存和 CPU 资源极大节省资源,尤其是在 I/O 密集型任务中
调度机制由操作系统调度由 JVM 管理和调度
六、使用虚拟线程的实际案例

在实际应用中,虚拟线程特别适合需要处理大量 I/O 操作的高并发场景。比如处理高并发 HTTP 请求,传统的线程池模型可能需要精心设计,而使用虚拟线程,开发者可以轻松启动数十万甚至数百万个线程来处理每个请求。

try (var server = HttpServer.create()) {server.createContext("/echo", exchange -> {var body = exchange.getRequestBody().readAllBytes();exchange.sendResponseHeaders(200, body.length);exchange.getResponseBody().write(body);});server.start();
}

在这个例子中,服务器为每个请求启动一个虚拟线程,从而消除了线程池的复杂性。

七、虚拟线程的优势与挑战

优势:

  • 简化并发编程:虚拟线程消除了开发者对线程池的依赖,能够以同步的方式编写代码,而不牺牲性能。
  • 更高的并发能力:虚拟线程允许更高数量的并发处理,尤其适用于 I/O 密集型任务。
  • 更低的系统资源占用:相比普通线程,虚拟线程占用的内存和 CPU 资源极少,可以在资源受限的环境中运行。

挑战:

  • 生态系统支持:虽然 Java 已经开始引入虚拟线程,但一些现有的 Java 框架和库可能还需要一段时间才能完全兼容虚拟线程。
  • 性能调优:尽管虚拟线程减少了创建和切换线程的开销,但在高密集 CPU 任务场景下,虚拟线程的调度仍然需要谨慎调优。
八、总结

虚拟线程的引入为 Java 开发带来了革命性的变化,特别是在高并发和 I/O 密集型场景中。与传统的普通线程相比,虚拟线程提供了更低的创建成本、更高的可伸缩性,并且简化了多线程编程模式。

在实际项目中,开发者可以根据任务的复杂性和并发需求,灵活选择使用普通线程还是虚拟线程。普通线程仍然适用于长时间运行的 CPU 密集型任务,而虚拟线程则是处理高并发、短期任务的最佳选择。随着 Java 虚拟线程的成熟,未来多线程编程将变得更加简洁、高效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/56239.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于摩托车一键启动无钥匙进入、智能科技创新

摩托车一键启动无钥匙进入功能 一、工作原理 摩托车的一键启动无钥匙进入功能采用了世界最先进的RFID无线射频技术和最先进的车辆身份编码识别系统&#xff0c;率先应用小型化、小功率射频天线的开发方案&#xff0c;并成功融合了遥控系统和无钥匙系统&#xff0c;沿用了传统…

在 MTT GPU 上使用 llama.cpp 推理

大语言模型因其出色的自然语言理解和生成能力而迅速被广泛使用&#xff0c;llama.cpp 大幅降低了进行大语言模型推理的门槛&#xff0c;MTT GPU 同样也是 llama.cpp 支持的运行平台&#xff0c;能够充分利用硬件的性能来助力用户的大语言模型应用。 本文主要介绍了如何在摩尔线…

【CTF】敏感信息泄露 GIT SVN VIM

在CTF&#xff08;Capture The Flag&#xff09;比赛中&#xff0c;信息泄露是常见的考察方向之一。这类题目通过模拟开发人员的疏忽或系统配置的失误&#xff0c;导致敏感文件或数据被泄露。信息泄露题目通常相对简单&#xff0c;但能帮助参赛者掌握如何从公开的信息中获取潜在…

出处不详 取数游戏

目录 取数游戏题目描述背景输入输出数据范围 题解解法优化 打赏 取数游戏 题目描述 背景 两人将 n n n个正整数围成一个圆环&#xff0c;规则如下&#xff1a; 第一名玩家随意选取数字&#xff1b;第二名玩家从与第一名玩家相邻的两个数字中选择一个&#xff1b;而后依次在…

用Arduino单片机制作一个简单的音乐播放器

Arduino单片机上有多个数字IO针脚&#xff0c;可以输出数字信号&#xff0c;用于驱动发声器件&#xff0c;从而让它发出想要的声音。蜂鸣器是一种常见的发声器件&#xff0c;通电后可以发出声音。因此&#xff0c;单片机可以通过数字输出控制蜂鸣器发出指定的声音。另外&#x…

【尚硅谷】FreeRTOS学笔记(更新中更新时间2024.10.12)

在网上看到的一段很形象的描述&#xff0c;放在这里给大家娱乐一下。 裸机开发&#xff1a;n个人拉屎&#xff0c;先进去一个拉完&#xff0c;下一个再来。看门狗&#xff1a;如果有人拉完屎还占着&#xff0c;茅坑刷视频&#xff0c;把他拖出去中断系统&#xff1a;n个人拉屎&…

Python | Leetcode Python题解之第477题汉明距离总和

题目&#xff1a; 题解&#xff1a; class Solution:def totalHammingDistance(self, nums: List[int]) -> int:n len(nums)ans 0for i in range(30):c sum(((val >> i) & 1) for val in nums)ans c * (n - c)return ans

数通--3

一、动态路由 内部 路由器之间要互联互通&#xff0c;必须遵循相同的协议 企业内部用 IGP&#xff0c;企业之间用BGP RIP&#xff08;已淘汰&#xff0c;不考&#xff09; 距离就是长短&#xff0c;矢量就是方向&#xff0c;即路由的出接口 一台路由器 A 配好RIP&#xff0c;…

SQL INNER JOIN:深入解析与实际应用

SQL INNER JOIN:深入解析与实际应用 引言 在关系型数据库管理系统中,SQL(Structured Query Language)是一种用于管理和操作数据库的标准编程语言。SQL INNER JOIN 是一种常用的查询技术,用于结合两个或多个数据库表中的相关行。本文将深入探讨 SQL INNER JOIN 的概念、语…

如何优化一个慢查询

从调用多次接口改为Batch批量查询 查询业务压力大小 查询数据库所在服务器CPU、内存占用查询数据库的连接数等 查询数据库的状态 https://blog.csdn.net/h2604396739/article/details/90521471/ 定位慢查询 https://blog.csdn.net/m0_54187478/article/details/136380207…

C++面试速通宝典——25

473. HTTP如何减少重定向请求 重定向请求&#xff1a; ‌‌‌‌  服务器上的一个资源可能由于迁移、维护等原因从url1移至url2后&#xff0c;而客户端不知情&#xff0c;他还是继续请求url1&#xff0c;这时服务器不能粗暴地返回错误&#xff0c;而是通过302响应码和Locati…

鸿蒙--商品列表

这里主要利用的是 List 组件 相关概念 Scroll:可滚动的容器组件,当子组件的布局尺寸超过父组件的视口时,内容可以滚动。List:列表包

Appium Device Farm安装教程

环境要求&#xff1a;Appium version ≥ 2.4.X 安装appium npm install -g appium2.11.3 如果安装提示如下问题 npm error code EEXIST npm error syscall rename npm error path /Users/wan/.npm/_cacache/tmp/d5787519 npm error dest /Users/wan/.npm/_cacache/content-…

鸿蒙--WaterFlow 实现商城首页

目录结构 ├──entry/src/main/ets // 代码区 │ ├──common │ │ ├──constants │ │ │ └──CommonConstants.ets // 公共常量类 │ │ └──utils │ │ └──Logger.ets // 日志打印类 │ ├──entryability │ │ └──EntryAbility.ets // 程序入口…

【2024最新】基于springboot+vue的体质数据分析及可视化lw+ppt

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

5、Spring Boot 3.x 集成 RabbitMQ

一、前言 本篇主要是围绕着 Spring Boot 3.x 与 RabbitMQ 的集成&#xff0c;这边文章比较简单&#xff0c;RabbitMQ 的集成没有太大的变化&#xff0c;这篇文章主要是为了后续的 RabbitMQ 的动态配置做铺垫。 1、Docker 安装 RabbitMQ 2、Spring Boot 3.x 集成 RabbitMQ二、D…

MPA-SVM多变量回归预测|海洋捕食者优化算法-支持向量机|Matalb

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译&am…

数据结构-5.6.二叉树的先,中,后序遍历

一.遍历&#xff1a; 二.二叉树的遍历&#xff1a;利用了递归操作 1.简介&#xff1a; 二叉树的先序遍历&#xff0c;中序遍历&#xff0c;后序遍历都是以根结点遍历顺序为准的&#xff0c;如先序遍历就先遍历根结点 2.实例&#xff1a; 例一&#xff1a; 例二&#xff1a; …

Rust 与生成式 AI:从语言选择到开发工具的演进

在现代软件开发领域&#xff0c;Rust 语言正在逐步崭露头角&#xff0c;尤其是在高性能和可靠性要求较高的应用场景。与此同时&#xff0c;生成式 AI 的崛起正在重新塑造开发者的工作方式&#xff0c;从代码生成到智能调试&#xff0c;生成式 AI 的应用正成为提升开发效率和质量…

FLINK SQL数据类型

Flink SQL支持非常完善的数据类型&#xff0c;以满足不同的数据处理需求。以下是对Flink SQL数据类型的详细归纳&#xff1a; 一、原子数据类型 字符串类型 CHAR、CHAR(n)&#xff1a;定长字符串&#xff0c;n代表字符的定长&#xff0c;取值范围为[1, 2147483647]。如果不指…