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

深入解析JAVA虚拟线程

摘要:通过修改JAVA基础类库和jvm,在不改变现有阻塞型编程习惯情况下引入虚拟线程,轻松实现io密集型的百万并发web应用。虚拟线程非常轻量,随用随扔,可以认为和创建一个普通的对象没有什么区别。本文深入研究虚拟线程的产生背景,原理,源码,特别是探究了Continuation 和VirtualThread 核心类,以及虚拟线程对ThreadLocal,Sleep()方法和OkHttp等的影响,可以让大家全方位了解虚拟线程。

注意,为了方便理解,本文的代码都在源码的基础上做了精简,只保留关键代码。

前言

自2014年的JDK8以后,JAVA 又有了几个LTS版本,JDK11(2018年),JDK 17(2021年),JDK21 (2023年),但是很多开发者的态度是,"你发你任你发,坚持用我的 JDK 8",认为后续的版本没有哪一个像 JDK8式的创新,因此没必要去学和改变。

但是JAVA已经有30年的历史,1:1的线程模型已经显得落后,让JAVA的单机并发能力比不上一些后来的语言,在高并发场景下需要的硬件成本也很贵,虽然目前也有了比如Spring Webflux这样的应用层的解决方案,但是需要搭配Project Reactor来使用地狱式的回调逻辑,指数增长的编程难度,很难推广使用。虚拟线程的正式引入让JAVA又焕发了第二春,让JDK21确实也是一个里程碑式的版本。

虚拟线程的使用起来却非常简单,下面举个例子来感受一下:

try (ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor()) {for (int i = 0; i < 100_0000; i++) {virtualExecutor.execute(() -> {// 执行业务 逻辑});}
}

如果是使用了Spring Boot 3,通过以下2个配置:轻松实现使用启用虚拟线程。

  1. 配置Executor,让Spring默认线程池改成虚拟线程。

    @Configuration
    @EnableAsync
    public class AsyncConfig {@Beanpublic Executor asyncExecutor() {// 替换掉 Spring 容器 中的默认线程池,每个 @Async 任务使用一个虚拟线程return Executors.newVirtualThreadPerTaskExecutor();}
    }
  2. application.yml

    server:servlet:thread:virtual: true   # MVC 请求使用虚拟线程
    

那么有了虚拟线程,有什么好处?可以简单在自己的电脑上测试如下代码:
平台线程(原始的线程):

public class TestPlatformThreads {public static void main(String[] args) {var counter = new AtomicInteger();while (true) {new Thread(() -> {int count = counter.incrementAndGet();System.out.println("Thread count = " + count);LockSupport.park();}).start();}}
}我的电脑测试结果,可以看到,很快就抛出异常了Thread count = 4062
[2.220s][warning][os,thread] Failed to start thread "Unknown thread" - pthread_create failed (EAGAIN) for attributes: stacksize: 2048k, guardsize: 16k, detached.
[2.220s][warning][os,thread] Failed to start the native thread for java.lang.Thread "Thread-4062"
Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reachedat java.base/java.lang.Thread.start0(Native Method)

虚拟线程:

public class TestVirtualThreads {public static void main(String[] args) {var counter = new AtomicInteger();while (true) {Thread.startVirtualThread(() -> {int count = counter.incrementAndGet();System.out.println("Thread count = " + count);LockSupport.park();});}}
}Thread count = 3592031
Thread count = 3592034
Thread count = 3592039虚拟线程轻松打印到几百万。

可以看到,虚拟线程的优势非常显著,同时虚拟线程设计得十分巧妙,对于开发者一直以来的阻塞式编程习惯,不需要任何修改,甚至精简了线程池的使用,上手非常快,从硬件角度,高并发情况下可以让CPU的时间更多放在业务的执行上,效率有了明显得提升,对内存的要求也更低一些,可以节省设备的成本。

本文深入研究虚拟线程的背景,原理,源码从多个角度来让大家了解虚拟线程。

背景

历史回顾

在了解虚拟线程以前,我们需要对java的线程发展历史做一下回顾。

1995 – JDK 1.0

  • Java 语言一开始就内置了 Thread 类和 Runnable 接口。

  • 那时,Java 的最大卖点之一就是“内置多线程支持”,相比 C/C++ 需要依赖系统 API 来写多线程,Java 直接提供了跨平台的抽象。

  • 这也是 Java slogan 里的 “Multithreaded” 来源之一。可以说,系统线程的包装,在当时是JAVA的特色。

早期实现(JDK 1.0 ~ JDK 1.2)

  • 不同操作系统上的 JVM 使用的线程模型不一样,有些 JVM 用的是 green threads(用户态线程),有些用 native threads(系统线程)

  • 例如:Solaris 上的早期 JVM 用 green threads,Windows 上的用 native threads。

JDK 1.3 开始(2000 年左右)

  • HotSpot JVM 成为主流实现,彻底抛弃了 green threads,全面改为 1:1 的 native threads(Java Thread ↔ OS Thread)

  • 从那之后,“Java 线程”基本上就等价于“操作系统线程”

JDK 19(2022, 预览)/ JDK 21(2023, 正式)

  • 引入了 虚拟线程(Virtual Threads),算是对 20 多年前被放弃的 green threads 的“现代化回归版”,但实现方式更高效、更贴合当前 JVM 和硬件环境。

  • 这时才引入了新的术语:

    • Platform Thread(旧的、直接对应 OS 的线程)

    • Virtual Thread(新的、轻量级的、JVM 调度的线程

  • 此时虚拟线程和系统相同线程的映射关系为M:N

平台线程的不足

那 Platform Thread(平台线程,旧版线程方式)存在什么问题呢?

  • 内存不足挑战

在Java中,每个Platform Thread(平台线程) 1:1 对应系统线程,默认情况下,jvm 分配给每个平台线程的虚拟机栈的大小是1M。也就是说,一台8g内存的设备,全部的内存用来创建平台线程,最多也只能生成 8192 条线程,设备的资源迅速就耗尽了。

  • CPU上下文切换挑战

即使我们加大了内存,比如升级到了几十个G的内存,看起来可以支持上万或者几十万的线程,但是大量创建的系统线程,会导致CPU大量的时间用在线程的上下文切换中,浪费了CPU的性能,真正执行业务的时间变少。

平台线程如何应对大量并发

在JAVA web应用开发中,如何应对高并发请求呢?

  • 使用线程池

    • 在Spring MVC中,常用的容器 Servlet 容器(Tomcat/Jetty)的线程池,工作线程的数量默认是200,请求数量超出部分在队列中等待。

    • 本质上是控制并发的数量

    • 如果想提升并发,需要堆机器

  • 直接使用中间件Netty

    • Netty 的本质是一套高性能网络通信框架,用少量线程支撑大量连接。

    • 核心原理就是基于 Reactor 模式,通过事件循环(EventLoop)和非阻塞 I/O,把所有网络事件用少量线程驱动,从而实现高并发和高吞吐。

    • 但是Netty 更偏向底层,如果完整应用是一个房子,Netty 只提供了地基,编写业务难度极大。

  • 使用框架 Spring Webflux

      特点

    • 使用 Reactive Streams 规范Publisher / Subscriber / Subscription / Processor)。

    • 基于 Project Reactor 实现 FluxMono,表示异步流。

    • 非阻塞:一个线程可以同时处理成千上万个请求。

    • 背压(Backpressure):消费者可以告诉生产者“别发太快”,避免内存爆炸。

    • 高吞吐、低延迟:尤其在 IO 密集型应用(HTTP 调用、DB 查询、消息队列)中表现突出。

        推广难点

  •         要写 Mono / Flux,理解 flatMap / switchIfEmpty / zip 等操作符,学习成本高
    • 需要转变以前的阻塞式编写习惯,在复杂业务条件下需要编写大量了回调式代码,地狱级体验。

    • 大量第三方库(数据库驱动、SDK、客户端 API)是 阻塞式的。WebFlux 要求 端到端异步,否则阻塞点会拖垮性能。例如:JDBC 是阻塞式 → 必须用 R2DBC 才能全异步。很多老的 HTTP Client、Redis、Kafka 驱动都是阻塞式的 → 需要替换。

    • 迁移成本非常高

关于 Spring Webflux,我想多讲几句,它是Spring团队2017推出的基于Platform Thread上的解决方案,可以看到他们一直想推广这个解决方案,在Spring boot很多场景中已经引入了很多FluxMono ,为全面转向Spring Webflux 做铺垫,但是因为需要修改编程习惯,而且需要大量第三方库进行更新支持,推广难度非常大,使用起来非常拧巴,基本宣告了从应用层解决百万高并发的努力是徒劳的,是失败的。特别是GO语言已经指明了方向的情况下,说明JAVA需要更深层次的革命,也就是在JDK层面进行革新。

引入虚拟线程

虚拟线程(Virtual Threads)是在JDK 19 中首次引入的预览特性,在JDK 21 (长期支持版本,2023 年 9 月 19 日发布)正式引入,是作为 Project Loom(织布机) 的核心成果。领导者是 Ron Pressler(Oracle,主要贡献集中在 Java 并发、虚拟线程、结构化并发、Continuation API 等方向,是 JVM 的重要核心开发者之一)。其他参与者主要是 OpenJDK 社区的开发者们(包括来自 Oracle、Red Hat、SAP 等公司的工程师),从参与者就可以看出虚拟线程是一个基于基础类库和JVM的根正苗红的特性。 姗姗来迟来的虚拟线程,为JAVA语言注入了新的生机。

关于 Project Loom ,可以参考 https://developer.okta.com/blog/2022/08/26/state-of-java-project-loom

Project Loom aims to drastically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications that make the best use of available hardware.

— Ron Pressler (Tech lead, Project Loom)

为什么说姗姗来迟呢?因为后来者GO语言在2009年发布的时候,直接支持了 Goroutine,而 java 语言类似虚拟线程的的概念,在9年后的Project Loom立项的时候才出现,经过了5年的开发和验证,在2023年引入JDK 21 ,才终于让JAVA赶上了GO语言的并发能力。

原理

无论是GO语言 ,还是 Spring Webflux,都说明了使用少量的系统线程完成百万

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

相关文章:

  • 不同设计牙周探针在深牙周袋探查中的精确性与局限性比较
  • 三极管分类
  • Leetcode 3710. Maximum Partition Factor
  • 亚马逊,塔吉特采购测评:高砍单率核心原因及技术破解策略
  • SQLite3数据库——Linux应用
  • 人机关系中“看不见的手”
  • 上街区网站建设做网站用什么系统好
  • k8s cert-manager cert-manager-webhook-xxx pod 证书过期问题处理
  • 宝塔服务器磁盘爆满:占用50G磁盘空间的.forever日志文件处理导致服务崩溃的教训
  • Docker资源限制全解析
  • 毫米级的安全舞蹈
  • 成都网站专业制作一造和一建哪个难度大
  • 解码AI智能体的大脑:Function Calling 与 ReAct 策略深度对决
  • K8s多租户方案指南--图文篇
  • 去一个新公司~重新设置git信息,clone项目 ~需要做的
  • wordpress 自动标签插件廊坊seo推广
  • Abase 数据库:永久关闭 misopt_preventing 选项的方法
  • 基于单片机的智能洗碗机设计
  • 网站策划书ppt9377白蛇传奇
  • 从Wireshark到Mitmproxy:网络数据侦探——抓包工具在爬虫开发中的艺术与科学之“HTTPS全流量解密实战”
  • HTTP与HTTPS的五大核心区别
  • 关于2025.10.13力扣每日的学习
  • Hive 删除分区语句卡死问题
  • 19.1 TCP 和 UDP 有什么区别?
  • 汇编和C语言结构
  • 单页网站的营销高端渠道开发
  • 定制网站建设公司哪家便宜seo免费课程
  • 【密码学实战】openHiTLS server命令行:搭建国密标准安全通信服务器
  • AWS RDS (MySQL)蓝绿部署常见误区
  • 工信部发布→《云计算综合标准化体系建设指南(2025版)》