文章目录
- 什么是虚拟线程?
- 平台线程是什么?
- 虚拟线程和平台线程有什么关系?
- 平台线程和系统内核线程
- 虚拟线程M:N的多路复用调度
- 虚拟线程有什么优点和缺点?
- [⚙️ 工作机制:JVM做到了协程式的魔法](#⚙️ 工作机制:JVM做到了协程式的魔法)
- 总结
什么是虚拟线程?
虚拟线程(Virtual Thread)是 JDK 而不是 OS 实现的轻量级(用户态)线程 (Lightweight Process,LWP),由 JVM 调度(管理)。许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量。能显著提升I/O密集型应用的并发处理能力
- 不是 OS 线程,是 JVM 用户态轻量级线程(本质就是 JVM 内置的协程)
- 编程模型:Thread API 完全兼容,写法和普通线程几乎一样
- 调度:JVM 自己调度 ,M:N 映射:大量虚拟线程 → 少量平台线程(载体线程)
- 栈极小,初始只有几 KB,创建几乎不耗资源,可以轻松开几十万、上百万个
- 遇到阻塞(查库、网络、sleep):虚拟线程主动挂起、让出载体线程,载体立马去执行别的虚拟线程,不浪费。
一句话:用同步阻塞的写法,跑出异步高并发的性能。
平台线程是什么?
在引入虚拟线程之前,java.lang.Thread 包已经支持所谓的平台线程(Platform Thread),也就是没有虚拟线程之前,我们一直使用的线程。
- 1:1 绑定操作系统原生线程
- 你 new 一个 Thread,操作系统就真给你开一个物理线程
- 栈内存默认 1MB左右,很重
- 创建慢、切换慢、数量有限,一般机器最多几千个就顶满
- 阻塞(sleep、查库、调接口)时:操作系统线程被卡死占用,干不了别的
总结:平台线程 Platform Thread就是我们以前一直用的普通 Java 线程。
虚拟线程和平台线程有什么关系?
JVM 调度程序通过平台线程(载体线程)来管理虚拟线程 ,一个平台线程可以在不同的时间执行不同的虚拟线程(多个虚拟线程挂载在一个平台线程上),当虚拟线程被阻塞或等待时,平台线程可以切换到执行另一个虚拟线程。
载体线程(Carrier Thread):被虚拟线程临时借用的平台线程。虚拟线程本身不能跑代码,必须 "挂载" 到一个平台线程上才能执行,这个平台线程就是该虚拟线程的载体。
平台线程和系统内核线程
在 Windows 和 Linux 等主流操作系统中,Java 线程采用的是一对一的线程模型,也就是一个平台线程对应一个系统内核线程。Solaris 系统是一个特例,HotSpot VM 在 Solaris 上支持多对多和一对一。
| 对比维度 | 虚拟线程 | 平台线程 |
|---|---|---|
| 💾 内存占用极低 (KB级),可创建数百万个 | 较高 (MB级),受限于系统资源 | |
| 🔄 调度单元(映射关系) | JVM,实现 M:N 调度模型 | 操作系统,实现 1:1 内核映射 |
| 😴 阻塞处理 | 自动挂起并释放载体线程,提高 CPU 利用率 | 阻塞并占用内核线程,可能造成线程饥饿 |
| 🛠️ 线程池 | 无需池化,每次都可直接创建新虚拟线程 | 需池化管理以复用昂贵的操作系统线程 |
| 🤝 适用场景 | I/O密集型任务(如高并发Web服务、数据库访问、微服务调用) | CPU密集型计算任务、需精细控制线程优先级或亲和力的场景 |
| 并发数量 | 百万级轻松 | 几千上限 |
| 创建开销 | 极轻(≈new Object) | 重(系统调用) |
虚拟线程M:N的多路复用调度
虚拟线程不绑定固定平台线程;只有正在运行的一瞬间,临时占用某个载体线程;
一旦遇到阻塞马上释放载体线程 ,阻塞结束后,虚拟线程重新排队,随便分配到任意一个空闲载体继续跑 。
传统平台线程(旧 Java 线程)是 1:1 映射:一个 Java 线程固定绑定一个操作系统内核线程,重量级、无法大量创建
虚拟线程有什么优点和缺点?
优点
- 非常轻量级:可以在单个线程中创建成百上千个虚拟线程而不会导致过多的线程创建和上下文切换。
- 简化异步编程: 虚拟线程可以简化异步编程,使代码更易于理解和维护。它可以将异步代码编写得更像同步代码,避免了回调地狱(Callback Hell)。
- 减少资源开销: 由于虚拟线程是由 JVM 实现的,它能够更高效地利用底层资源,例如 CPU 和内存。虚拟线程的上下文切换比平台线程更轻量,因此能够更好地支持高并发场景。
缺点
- 不适用于计算密集型任务: 虚拟线程适用于 I/O 密集型任务,但不适用于CPU计算密集型任务,因为密集型计算始终需要 CPU 资源作为支持。
- 与某些第三方库不兼容: 虽然虚拟线程设计时考虑了与现有代码的兼容性,但某些依赖平台线程特性的第三方库可能不完全兼容虚拟线程。
- 不能用 ThreadLocal 滥用(虚拟线程量大,内存泄漏风险高)
注意synchronized 会导致 Pinning(JDK21 痛点)
JDK21 中:synchronized块会把虚拟线程钉死在当前载体线程
一旦阻塞:载体被占死,无法复用,退化成传统线程池
解决:
尽量用 java.util.concurrent.locks.ReentrantLock替代 synchronized
升级到 JDK24:已修复 synchronized Pinning 问题
⚙️ 工作机制:JVM做到了协程式的魔法
虚拟线程的核心是:阻塞时让出载体 ,提高利用率。
当虚拟线程执行可能阻塞的 I/O 操作(如数据库查询、网络请求)时,JVM 会执行一个对开发者透明的"魔法",整个过程被称为 "挂载(Mount)/ 卸载(Unmount)"。
- 检测与卸载:虚拟线程遇到阻塞操作时,JVM 会保存其当前的执行状态(包括栈帧、程序计数器等),然后将其从载体线程上"卸载"。
- 释放与复用:被释放的载体线程会立刻被调度去执行另一个就绪的虚拟线程,从而实现高效的 CPU 利用。
- 唤醒与恢复:当 I/O 操作完成(或被唤醒),虚拟线程会被重新"挂载"到一个(可能是新的)空闲载体线程上,并从之前保存的状态继续执行。
这一切都依赖于JVM底层的 Continuation 机制,它赋予了代码"暂停"和"恢复"的能力,是实现虚拟线程技术的基石。
总结
JDK21 虚拟线程是 JVM 内置的轻量级协程,采用 M:N 复用载体线程(平台线程),创建开销极低、支持百万级并发,完美适配 IO 密集型场景,同步阻塞写法即可跑出高并发性能,无需线程池调优;但不适合 CPU 密集,JDK21 下要慎用 synchronized。