虚拟线程是 Java 语言中实现的一种轻量级线程,在 Java 项目中可以减少编写、维护和调试高吞吐量并发应用程序的工作量。
有关虚拟线程的背景介绍,大家可以参阅 JEP 444。
https://openjdk.java.net/jeps/444
在操作系统中,线程是其可调度的最小处理单元。同一时刻会有很多线程同时运行,但它们之间基本相互独立运行。
在 Java 中,操作线程的相关 API 都在 java.lang.Thread 类中。在 Java 21 以后,线程有两种,一种是平台线程,一种是虚拟线程。
本文大纲
图片
什么是平台线程?
平台线程被实现为操作系统线程的简单包装器。平台线程上运行的 Java 代码,在其底层逻辑上,其实就是运行在操作系统的线程上,并且平台线程在其整个生命周期内都与操作系统线程一一对应。
图片
因此在 Java 项目中,可用平台线程的数量依赖于操作系统线程的数量。根据操作系统和 JVM 启动参数配置的不同,创建一个平台线程默认会消耗 1 MB 的空间。
因此平台线程的资源相当宝贵,我们无法大量的创建平台线程。
在 Java 21 虚拟线程出来以前,可能大家都没听说过平台线程,那么到底什么是平台线程嘞?这里我给大家一句话解释清楚。
在 Java 21 虚拟线程出来以前,Java 所提供的线程都是平台线程,一个线程对应一个操作系统线程,并且平台线程很珍贵,不能大量创建。(大家为了节约线程资源,也就有了线程池的概念)
什么是虚拟线程?
与平台线程一样,虚拟线程也是 java.lang.Thread 的一个实例对象。但是,虚拟线程并不依赖于特定的操作系统线程。
虚拟线程底层仍然在操作系统的线程上运行代码。但是与平台线程不相同的是,在平台线程中运行的代码调用阻塞 I/O 操作时,JVM 就会挂起该平台线程(也就会挂起操作系统线程),直到阻塞 I/O 可以恢复为止。
而在虚拟线程中调用阻塞 I/O 操作时,JVM 虽然也会挂起该虚拟线程,但是与平台线程不同的是,被挂起虚拟线程关联的操作系统线程是可以为其他虚拟线程继续服务的。
图片
虚拟线程的实现方式与虚拟内存类似。为了模拟大量内存,操作系统将较大的虚拟地址空间映射到有限的 RAM。同样,为了模拟大量线程,Java 运行时将大量虚拟线程映射到少量操作系统线程。
因此与平台线程消耗的资源很多不同,虚拟线程在使用时只需要很少的内存资源。单个 JVM 就可以轻松创建数百万个虚拟线程。
使用虚拟线程是,通常建议调用堆栈不要过深,只执行单个 HTTP 客户端调用或单个 JDBC 查询即可。尽管虚拟线程支持线程局部变量和可继承的线程局部变量,但我们应该仔细考虑后再使用它们,因为单个 JVM 可能运行数百万个虚拟线程。
虚拟线程适合用于运行有大量阻塞 I/O 操作的任务,而不是长时间运行的 CPU 密集型任务。
为什么建议使用虚拟线程?
在高吞吐量、高并发应用程序中推荐使用虚拟线程,尤其是那些包含大量并发任务且大部分时间都在等待阻塞 I/O 操作的应用程序中。
使用虚拟线程可以让应用程序就算使用同步阻塞 API,也能对操作系统的硬件资源利用达到近乎完美水平。
可以说,虚拟线程的引入,以后程序员就算是使用 Java 中同步阻塞 API 也可以开发出高性能、高吞吐量的应用程序。
最后总结一下,虚拟线程是一种轻量级线程,带给了程序员一种新的编程体验。
编写高性能、高吞吐量应用程序时使用虚拟线程配合同步阻塞 API 就能得到与异步编程模型相媲美的性能,并且避免了异步编程模型的编程复杂度。比如在 Spring Boot3.2 中,Tomcat 就可以通过启用虚拟线程带来可观的性能提升。