IWA
2025-11-21
点 赞
0
热 度
3
评 论
0

Java Virtual Threads(虚拟线程)教程:调度机制与底层原理深度解析(第 3 篇)


系列:《Java Virtual Threads(虚拟线程)教程:从零上手到实战优化》


第 3 篇:虚拟线程调度机制深度解析:平台线程、纤程、栈帧、挂起与恢复

本篇是整个系列中最重要的底层文章之一。
如果你理解了 Virtual Threads 的运行机制,你就能完全理解为什么“同步写法 = 异步性能”。


目录

  1. 虚拟线程究竟由谁执行?(平台线程 vs 虚拟线程)

  2. 调度器是怎样工作的?(ForkJoinPool 的新模式)

  3. 虚拟线程阻塞时到底发生了什么?(挂起、保存栈帧)

  4. 栈管理:为什么虚拟线程只有几十 KB?

  5. Pinning:唯一会让虚拟线程变慢的情况

  6. 为什么虚拟线程不会提升 CPU 性能?

  7. 执行流程:从提交任务到执行完成的全生命周期图

  8. 本篇总结


一、虚拟线程究竟由谁执行?

这是理解 Virtual Threads 的关键。

♦ 传统线程模型

Java Thread 1 ----> OS Thread 1  
Java Thread 2 ----> OS Thread 2  
Java Thread N ----> OS Thread N  

一个 Java Thread 就对应一个 OS Thread,资源消耗大、切换开销高。


♦ 虚拟线程模型(Fiber 模型)

Virtual Threads(100000+)  
       ↓(调度)
Platform Threads(几十个)  
       ↓
CPU 核心

虚拟线程由少量“平台线程”执行。

平台线程就是 OS Thread,但数量极少,比如:

  • 8 核 CPU → 可能只有 16~32 个平台线程

虚拟线程只是运行在平台线程上的一个“任务”,它的状态由 JVM 管理。


二、虚拟线程调度器到底是谁?

虚拟线程调度器由 Java 内置的 ForkJoinPool(新模式) 提供,但不是经典的 Fork/Join 线程池。

区别:

机制

用途

传统 ForkJoinPool 模式

工作窃取、分治任务

Virtual Thread Scheduler(新模式)

只负责执行虚拟线程,不再进行分而治之

虚拟线程的调度过程非常简单:

任务来了 → 派给一个平台线程执行 → 遇到阻塞?挂起 → 派给下一个虚拟线程

用最小的线程数,执行最多的任务。


三、虚拟线程阻塞时到底发生了什么?(关键知识点)

这是理解性能提升的核心 —— “阻塞是廉价的”

假设有一个虚拟线程:

Thread.sleep(2000);

传统线程的行为:

  • Java Thread/OS Thread 都“睡着”

  • 占用内核资源,不可调度

  • 影响吞吐量


虚拟线程的行为:

  1. 虚拟线程执行到 sleep → JVM 检测到阻塞调用

  2. 虚拟线程被挂起(park)

  3. 此时该虚拟线程的栈帧会被保存到 heap 或结构化栈中

  4. 占用该平台线程的执行权被释放

  5. 平台线程马上去执行其他虚拟线程(几乎无成本)

虚拟线程(挂起)
平台线程(继续工作)

结论:阻塞不再占用平台线程。因此同步代码也能达到异步的吞吐性能。


四、虚拟线程的栈是如何管理的?为什么它如此轻量?

平台线程的栈是:

  • 固定大小(一般 1MB)

  • OS 分配

  • 内存浪费严重

虚拟线程的栈则完全不同:


♦ 特性:栈是“动态增长 + 分段存储”

虚拟线程使用 栈片(Stack Chunk) 技术:

  • 每个 chunk 只有几 KB

  • 栈多了再增加 chunk(链式结构)

  • 阻塞时只需要保存当前活跃栈片

[Chunk 1] → [Chunk 2] → [Chunk 3] ...

因此:

  • 空闲虚拟线程消耗极低

  • 可轻松创建百万级线程

  • 栈帧保存和恢复超快


五、Pinning:唯一可能让虚拟线程变慢的情况(重要)

Pinning = 假设虚拟线程正在执行某个必须绑定到平台线程的代码段,这时它就无法挂起。

虚拟线程会“钉在平台线程上”,无法释放。

哪些情况会发生 Pinning?

1. 使用 synchronized

synchronized(obj) {
    Thread.sleep(1000);  // pinning
}

因为 synchronized 依赖平台线程的 monitor。


2. native 方法阻塞

如:

  • File I/O(旧版)

  • socket 调用(部分场景)

  • system calls


3. 传统 Blocking I/O(如 FileInputStream.read())

JDK 21 已优化大部分网络 I/O,但本地文件可能仍导致 pinning。


Pinning 的影响

  • 虚拟线程被固定到某个平台线程

  • 阻塞期间平台线程无法执行其他虚拟线程

  • 吞吐性能下降


如何避免?

  • 使用 java.util.concurrent 的锁,而不是 synchronized

  • 使用 Java NIO 或JDK21+ 的优化 I/O(大部分已解决)

  • 避免 native 阻塞


六、为什么虚拟线程不能提升 CPU 性能?

虚拟线程适合 I/O 密集型,不适合 CPU 密集型。

原因:

  • CPU 密集型任务不会阻塞

  • 虚拟线程无法“挂起”

  • 最终执行速度仍然取决于 CPU 核心数

如果你跑矩阵乘法、加密计算等 CPU-heavy 任务:

  • 虚拟线程 ≈ 传统线程

  • 最多跑满 CPU 核心,不可能更快


七、虚拟线程完整生命周期(流程图)

                 创建 Virtual Thread
                          │
                          ▼
              提交到调度器(Scheduler)
                          │
                          ▼
                分配给平台线程执行
                          │
                          ▼
           ┌─────────────阻塞调用─────────────┐
           │                                   │
           ▼                                   ▼
   是否可安全挂起?                        Pinning(不可挂起)
           │                                   │
       是  ▼                                   ▼  否
     保存栈帧                             持续占用平台线程
     释放平台线程                          性能下降
           │
           ▼
      等待恢复
           │
           ▼
      恢复栈帧 → 重新派发到平台线程 → 继续执行
           │
           ▼
         结束

八、本篇总结

通过本篇,你应该已经理解:

✔ 虚拟线程运行在少量平台线程上

✔ 阻塞时虚拟线程会挂起,不占用平台线程

✔ 栈以“分段方式”保存,因此虚拟线程极轻

✔ 唯一性能陷阱是 Pinning(比如 synchronized)

✔ 虚拟线程适合 I/O 密集,不是 CPU 密集

✔ 调度器本质是 ForkJoinPool 的增强模式

这些原理解释了:

为什么 Java 可以在不改变同步写法的前提下,做到异步级别的吞吐性能。

你成为虚拟线程专家的核心基础已经打好。


下一篇预告(第 4 篇)

《Spring Boot 与虚拟线程:线程池、Web 请求、RestTemplate、WebClient 全面迁移指南》

内容将包括:

  • Spring Boot 如何启用虚拟线程?

  • 如何让 Controller → Service → Repository 全链路使用虚拟线程?

  • 数据库连接池如何配合虚拟线程?

  • 虚拟线程 + Tomcat / Undertow / Netty 的对比

  • 全代码实战:重写一个同步 REST 服务,让性能提升数倍



用键盘敲击出的不只是字符,更是一段段生活的剪影、一个个心底的梦想。希望我的文字能像一束光,在您阅读的瞬间,照亮某个角落,带来一丝温暖与共鸣。

IWA

infp 调停者

具有版权性

请您在转载、复制时注明本文 作者、链接及内容来源信息。 若涉及转载第三方内容,还需一同注明。

具有时效性

文章目录

IWA的艺术编程,为您导航全站动态

34 文章数
9 分类数
10 评论数
32标签数
最近评论
IWA

IWA


👍

M丶Rock

M丶Rock


😂

M丶Rock

M丶Rock


感慨了

M丶Rock

M丶Rock


厉害了

M丶Rock

M丶Rock


6666666666666666666