Java 中的虚拟线程,也叫做协程或“轻量级线程”,它诞生于 JDK 19(预览 API),正式发布于 JDK 21,它是一种在 Java 虚拟机(JVM)层面实现的逻辑线程,不直接和操作系统的物理线程一一对应 操作系统线程、普通线程(Java 线程)和虚拟线程的关系如下:虚拟线程使用虚拟线程的创建方式,主要有以下 4 种:Thread.startVirtualThread(Runnable task)Thread.ofVirtual (()->{ System.out.println("Do virtual thread.");});// 运行虚拟线程vt.start();3.factory先创建虚拟线程工厂,然后再使用工厂创建虚拟线程 普通线程默认创建的是用户线程(而守护线程),而虚拟线程是守护线程,并且其守护线程的属性不能被修改,如果修改就会报错,如下图所示:虚拟线程由 JVM 调度和使用,避免了普通线程频繁切换的性能开销,所以相比于普通的线程来说 小结线程是轻量级的进程,而虚拟线程则是轻量级的线程,虚拟线程是 JVM 层面实现的逻辑线程,不直接和操作系统的物理线程一一对应,因此使用它可以减少线程上下文切换所带来的性能开销。
Spring WebClient 与虚拟线程:实战集成指南 在上一篇文章中,我们介绍了 JDK HttpClient 和虚拟线程的基础概念。 与 Spring WebClient 集成 你也可以增强 Spring WebClient,使其使用虚拟线程,将 Spring 强大的生态系统与虚拟线程的可扩展性结合起来。 这样你既能享受 Spring 的便利,又能获得虚拟线程的性能优势。 虚拟线程消除了简单性(每个请求一个线程)与可扩展性之间的传统权衡。 虚拟线程使这种模式高效且简单。
Java23下载地址:https://www.oracle.com/cn/java/technologies/downloads/虚拟线程的特点 轻量级每个虚拟线程的创建和销毁成本非常低,可与协程的成本相媲美 阻塞与非阻塞兼容 虚拟线程支持阻塞式代码风格,即便线程处于阻塞状态,也不会浪费操作系统资源,因为虚拟线程在阻塞时会自动挂起并释放底层资源。同时兼容现有的非阻塞 I/O,开发者无需改变已有的代码习惯。 透明性 使用虚拟线程无需学习新的 API,开发者可以像使用传统线程一样简单地操作虚拟线程。 每个虚拟线程可以由一个或多个操作系统线程动态映射和执行,从而提升资源利用率。虚拟线程的应用场景 高并发网络应用 适用于需要处理大量用户请求的服务器,例如 Web 服务、聊天系统等。 如何使用虚拟线程从 JDK 19 开始,虚拟线程作为预览特性引入,JDK 21 及之后版本正式稳定。
虚拟线程(Virtual Threads):什么是JDK 21中的虚拟线程?如何实现轻量级线程管理? 引言 在Java中,传统线程由操作系统管理,虽然功能强大,但开销较大,无法高效处理海量并发任务。 核心概念:什么是虚拟线程? 1. 定义与特性 虚拟线程(Virtual Threads) 是一种由JVM而非操作系统直接管理的线程。 启用虚拟线程的环境要求 虚拟线程是JDK 21正式发布的新特性,确保以下环境支持: JDK 21及以上版本。 --enable-preview 标志以启用虚拟线程。 2. 阻塞操作:避免在虚拟线程中执行耗时的阻塞操作,如锁或长时间计算。 Q&A互动答疑 Q:虚拟线程可以完全替代传统线程吗? A:虚拟线程适合高并发任务,但在一些场景(如与本地代码交互、计算密集型任务)仍需使用传统线程。 Q:虚拟线程是否线程安全?
介绍 在本文中,我们将深入探讨所有同级产品之间的友好比较,即具有「物理线程、虚拟线程和 Webflux 的 SpringBoot」 ,重点关注它们在特定用例场景中的性能。 —它在虚拟线程而不是传统的物理线程上运行。 虚拟线程是并发领域的游戏规则改变者。这些轻量级线程简化了开发、维护和调试高吞吐量并发应用程序的复杂任务。 虽然虚拟线程仍然在底层操作系统线程上运行,但它们带来了显着的效率改进。 当虚拟线程遇到阻塞 I/O 操作时,Java 运行时会暂时挂起它,从而释放关联的操作系统线程来为其他虚拟线程提供服务。这个优雅的解决方案优化了资源分配并增强了整体应用程序响应能力。 、虚拟线程和 Webflux 进行友好性能评估的背景下,了解关键的数据关系至关重要。
载体线程(Carrier Thread):指真正负责执行虚拟线程中任务的平台线程。一个虚拟线程装载到一个平台线程之后,那么这个平台线程就被称为虚拟线程的载体线程。 **这意味着许多虚拟线程可以在同一个平台线程上运行他们的 Java 代码,共享同一个平台线程。**同时虚拟线程的成本很低,虚拟线程的数量可以比平台线程的数量大得多。 unmount 操作:虚拟线程从平台线程卸载,此时虚拟线程的任务还没有执行完成,所以虚拟线程中包装的 Continuation 栈数据帧会会留在堆内存中。 从 Java 代码的角度来看,其实是看不到虚拟线程及载体线程共享操作系统线程的,会认为虚拟线程及其载体都在同一个线程上运行,因此,在同一虚拟线程上多次调用的代码可能会在每次调用时挂载的载体线程都不一样。 一个虚拟线程被分配平台线程,该平台线程作为载体线程执行虚拟线程中的任务。虚拟线程运行其 Continuation,Mount(挂载)平台线程后,最终执行 Runnable 包装的用户实际任务。
虚拟线程是一种在 Java 虚拟机(JVM)层面实现的逻辑线程,不直接和操作系统的物理线程一一对应,因此它可以减少上下文切换所带来的性能开销。 操作系统线程、普通线程(Java 线程)和虚拟线程的关系如下:1.虚拟线程使用虚拟线程的创建有以下 4 种方式:Thread.startVirtualThread(Runnable task)Thread.ofVirtual 普通线程默认创建的是用户线程(而守护线程),而虚拟线程是守护线程,并且其守护线程的属性不能被修改,如果修改就会报错,如下图所示:虚拟线程由 JVM 调度和使用,避免了普通线程频繁切换的性能开销,所以相比于普通的线程来说 PS:这里是虚拟线程执行器,不是虚拟线程池。 有了虚拟线程后还需要虚拟线程池吗?为什么?
:体验 支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用 本篇概览 本篇是《支持JDK19虚拟线程的web框架》系列的第三篇,在前面两篇咱们一起了解和体验了支持虚拟线程的 web服务,功能性能都试过,整个开发过程也完整执行,算是对quarkus和虚拟线程有了初步的了解,但也留下两个问题 虚拟线程和常规子线程的区别,究竟能不能看出来? 不使用虚拟线程时的线程状况 咱们先发请求到/pool/persons,也就是先不用虚拟线程,看看传统线程池响应web服务的时候,在JProfiler中是啥样的 像《上篇》那样,用K6压测接口/pool 相信您已经很清楚了吧,我觉得是这三样: 调度器,scheduler(ForkJoin线程池中的线程) 执行虚拟线程任务的真实线程,carrier 虚拟线程 现在开始压测吧,继续用k6,如下图,脚本中的地址要改成使用虚拟线程的 来完成虚拟线程中的任务,等到这些任务执行完毕,所有真实线程、虚拟线程都被结束,不再存活 至此,借助JProfiler观察常规线程和虚拟线程的实战就完成了,经过了这些理论结合实际的操作和分析,相信您对虚拟线程的认知已经更具体和全面
虚拟线程和普通线程的区别 “虚拟”线程,望文生义,它是“假”的,它不直接调度操作系统的线程,而是由 JVM 再提供一层线程的接口抽象,由普通线程调度,即一个普通的操作系统线程可以调度成千上万个虚拟线程。 虚拟线程比普通线程的消耗要小得多得多,在内存足够的情况下,我们甚至可以创建上百万的虚拟线程,这在之前(Java19 以前)是不可能的。 SpringBoot 使用虚拟线程 下面我们会在 SpringBoot 中使用虚拟线程,将默认的异步线程池和 http 处理线程池替换为虚拟线程,然后对比虚拟线程和普通线程的性能差异,你会发现差别就像马车换高铁 要在 SpringBoot 中使用虚拟线程很简单,增加如下配置即可: /** * 配置是用于稍后测试,spring.virtual-thread=true是使用虚拟线程,false时还是使用默认的普通线程 下面再看下虚拟线程的表现: 「虚拟线程耗时」: 可以看到即使是最大耗时,也保持在 100ms 以下,即线程等待时间显著的减少,虚拟线程更好的利用了系统资源。
Virtual Threads(虚拟线程)是Java平台的一项新功能,它旨在改进Java中的并发编程模型。传统上,Java使用基于操作系统线程的并发模型,每个线程都需要分配一个操作系统线程来执行。 具体而言,当一个Java应用程序创建一个Virtual Thread时,JVM会为其分配一个虚拟线程(也称为轻量级线程)。这些虚拟线程由协作调度器管理,并在需要时与操作系统线程进行绑定。 协作调度器负责决定哪个虚拟线程可以运行以及何时切换虚拟线程。 Fork/Join框架是Virtual Threads的另一个关键组件。 然后,我们通过调用execute()方法提交了一系列任务,每个任务都会打印当前运行的虚拟线程的名称。 7. 它通过引入轻量级的虚拟线程,并利用协作调度器和Fork/Join框架来提供高效的并发执行。
简单来说,以前你要么写复杂的异步代码,要么忍受线程资源限制,现在虚拟线程让你鱼和熊掌兼得。 这就像传统线程是雇佣全职员工,成本高;虚拟线程是雇佣临时工,按需分配,成本低。 当将 JDK HttpClient 与虚拟线程集成时,魔法就发生了。 MB 内存(性能好,但代码复杂) • 虚拟线程 + HttpClient:3-4 秒,300 MB 内存(性能好,代码简单) 虚拟线程实现了响应式级别的性能,同时代码更易于阅读且是命令式的。 这就是虚拟线程的魅力所在——用同步代码的简洁性,获得异步代码的性能。
(大家为了节约线程资源,也就有了线程池的概念) 什么是虚拟线程? 与平台线程一样,虚拟线程也是 java.lang.Thread 的一个实例对象。但是,虚拟线程并不依赖于特定的操作系统线程。 虚拟线程底层仍然在操作系统的线程上运行代码。 而在虚拟线程中调用阻塞 I/O 操作时,JVM 虽然也会挂起该虚拟线程,但是与平台线程不同的是,被挂起虚拟线程关联的操作系统线程是可以为其他虚拟线程继续服务的。 虚拟线程的实现方式与虚拟内存类似。 因此与平台线程消耗的资源很多不同,虚拟线程在使用时只需要很少的内存资源。单个 JVM 就可以轻松创建数百万个虚拟线程。 尽管虚拟线程支持线程局部变量和可继承的线程局部变量,但我们应该仔细考虑后再使用它们,因为单个 JVM 可能运行数百万个虚拟线程。
:体验 支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用 支持JDK19虚拟线程的web框架,之三:观察运行中的虚拟线程 本篇概览 本篇是《支持JDK19虚拟线程的 另外请放心,虽然quarkus源码复杂,但本文会做到十分克制,不会在虚拟线程之外的地方展开阅读和分析,以保证整篇文章都在聚焦虚拟线程, 本文主要由下图的内容构成,红色区域表示本篇核心:一个特别的Excutor executor.submit方法,这样就创建了虚拟线程,并在虚拟线程中执行业务逻辑: 现在去看创建VIRTUAL_EXECUTOR_SUPPLIER的代码就会特别清晰了,如下图,前面在JDK官方指导看到的 ,就会返回一个可用的executor,确保业务能执行下去 对于上图箭头3位置的做法,个人并不认同:我使用虚拟线程,就是想一口气创建成千上万线程,再肆无忌惮的使用,遇到不支持虚拟线程的场景,直接抛异常让我知道这条路走不通 后面更精彩 下一篇文章就是整个系列的终篇了,相比本文,终篇会简单很多,大家一起在轻松的氛围中畅谈线程技术的一个重要成员:ThreadLocal,看它在虚拟线程时代如何兴风作浪 你不孤单,欣宸原创一路相伴
虚拟线程简介2.1 虚拟线程的定义和概念虚拟线程是Java 21引入的一种轻量级线程实现,旨在提供更高效的多线程处理方式。 2.2 虚拟线程如何提高应用程序的性能和资源利用率虚拟线程通过以下方式提高应用程序的性能和资源利用率:任务复用: 虚拟线程通过共享线程池中的线程,避免了重复创建和销毁线程的开销,提高了资源的复用率。 虚拟线程的使用方式3.1 如何创建和管理虚拟线程虚拟线程的创建和管理相对简单,使用Thread.ofVirtual().start()来启动虚拟线程。 以上是虚拟线程的基本使用方式,通过这些API和机制,开发者能够更加灵活地管理虚拟线程的生命周期、状态,以及利用ForkJoinPool来实现高效的虚拟线程调度和执行。 在下一节中,我们将深入讨论虚拟线程与传统线程在不同方面的对比,帮助开发者更好地理解虚拟线程的特性。小结虚拟线程是Java 21引入的创新性特性,旨在优化多线程编程。
这是一个用于线程处理的 API,它提供了一种将协作任务(通常是虚拟线程)作为子任务集合进行集体考虑和管理的方法。 并发编程中的一类问题称为数据并行问题。 API 与虚拟线程的密切关系意味着它主要适用于涉及一定量 I/O 的任务(尤其是对远程服务的调用)。然而,该方法对于仅(或主要)作用于内存数据的操作不太有用,因为虚拟线程将相互竞争 CPU 时间。 结构化并发任务的一般流程如下所示: 创建任务作用域范围——创建线程拥有该范围(上下文)。 在作用域内分叉并发子任务(每个子任务都是一个虚拟线程)。 没有任何软件工具能百分之百准确地判断一个线程是否适合转换为虚拟线程 vthread--这是人类软件工程师的任务。 ,但作用域值与虚拟线程模式(如 fire-and-forget 模式)结合得很好。
jdk 21中的虚拟线程已经推出好一段时间了,确实很轻量,先来一段示例: 假如有一段提交订单的业务代码: 1 public void submitOrder(Integer orderId) { 10]; 4 StopWatch watch = new StopWatch(); 5 watch.start(); 6 //下10个订单(这里使用虚拟线程 这里有一个坑:正所谓“成也萧何,败也萧何”,虚拟线程这种轻量性的机制,导致它创建起来成本太低了,完全没有池化的必要,1台机器轻轻松松就可以瞬间创建上万甚至上百成的虚拟线程。 相比传统的线程池机制(有coreSize,maxSize,workQueue缓冲,以及各种policy策略兜底),虚拟线程完全没有并发度控制的概念,如果瞬间生成大量的虚拟线程,每个里都是执行db/redis 因此,实际生产应用中,强烈建议大家控制虚拟线程的执行并发数!!!
虚拟线程与传统线程的对比 1.1 内存消耗 传统线程: 每个传统线程都需要独立的内存分配,导致较大的内存消耗。 虚拟线程: 共享线程池,避免为每个任务分配独立内存,显著降低内存开销。 1.4 虚拟线程适用场景 虚拟线程在以下场景下更为适用: 大量细粒度任务: 由于虚拟线程的轻量级特性,适合处理大量细粒度的任务,提高了并发度和整体性能。 虚拟线程的异步编程模型 2.1 简化异步编程 轻量级启动: 虚拟线程的快速启动和低开销使得异步任务的启动变得更为高效,降低了异步任务启动的成本。 任务复用: 虚拟线程通过共享线程池,避免了为每个异步任务创建新线程的开销,提高了资源的复用率。 开发者可以通过这些示例了解虚拟线程的创建、任务执行以及如何在任务分解中充分利用虚拟线程的特性。根据实际需求,还可以进一步探索虚拟线程在更复杂场景下的使用。
多线程的性能陷阱线程创建和切换开销:每个线程的创建需要分配栈空间(通常8MB)、线程控制块(TCB)等资源。 进程与线程的本质区别进程:资源的容器进程是操作系统进行资源分配的基本单位,它提供了一个执行环境,包括:独立的地址空间:每个进程有自己的虚拟地址空间,通过页表隔离资源句柄表:文件描述符、信号处理程序、用户权限等执行上下文 ;structmm_struct{pgd_t*pgd;//页全局目录(页表根)structvm_area_struct*mmap;//虚拟内存区域链表atomic_tmm_users;//使用该地址空间的线程数 共享代码段、数据段,减少内存冗余虚拟内存:现代操作系统的基石虚拟地址vs物理地址基本区别特性虚拟地址物理地址可见性进程可见硬件可见连续性逻辑连续物理不连续大小由架构决定(48/57位)由RAM大小决定转换需通过 虚拟内存不仅仅是隔离进程的工具,更是现代操作系统实现高效、安全、灵活内存管理的核心机制。
虚拟线程是轻量级的,这意味着它们可以比传统线程创建更多数量,并且开销要少得多。这使得在自己的线程中运行单独任务或请求变得更加实用,即使在高吞吐量的程序中也是如此。 欢迎关注博客连载的Java新特性专栏:https://www.didispace.com/java-features/ 创建和使用虚拟线程 在Java 21中创建和使用虚拟线程有多种方法: 1. 所以,对于虚拟线程的概念,你只需要有一个基本的认识。所以,在文章的最后,做一个小结,以方便大家理解和记忆: 虚拟线程是由JVM管理的轻量级线程。 虚拟线程不需要任何显式分配或调度。 虚拟线程非常适合I/O密集型任务或需要大量并行性的任务。 虚拟线程也可以用来实现异步操作。 另外,值得注意的是,虽然虚拟线程可以在并发性和可扩展性方面提供显着的帮助,但它们并不总是适合所有场景。 有些需要大量计算的任务,并不一定在虚拟线程中运行更好,因为虚拟线程也有上下文切换的开。具体情况还是需要通过测试评测,以找到最优解。
/RISC-V Port[2] JEP 424: Foreign Function & Memory API(外部函数和内存 API)[3](预览) JEP 425: Virtual Threads(虚拟线程 JEP 425: 虚拟线程(预览) 虚拟线程是 JDK 而不是 OS 实现的轻量级线程(Lightweight Process,LWP),许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量 虚拟线程在其他多线程语言中已经被证实是十分有用的,比如 Go 中的 Goroutine、Erlang 中的进程。 虚拟线程避免了上下文切换的额外耗费,兼顾了多线程的优点,简化了高并发程序的复杂,可以有效减少编写、维护和观察高吞吐量并发应用程序的工作量。 StructuredTaskScope 的基本用法如下: 结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。许多虚拟线程共享同一个操作系统线程,从而允许非常多的虚拟线程。