Java 平台诞生已接近 30 年,但始终稳居最受欢迎的编程语言排行榜前三名。这背后的一项关键原因就是 Java 虚拟机 (JVM)。通过对内存管理等问题进行抽象化处理,以及在运行过程中编译代码,JVM 能够提供其他运行时环境无法企及的互联网级可扩展性。
Java 如此受欢迎还得益于语言本身、库和 JVM 的进化速度。2019 年,OpenJDK(用于进行 Java 开发的开源项目)从基于功能的发布模式转变为采用基于时间的发布计划。这意味着,如今我们每年都能迎来两个新的 Java 版本,无需等待两到四年。
鉴于版本发布如此频繁,让发行版为所有版本提供长期维护和支持是不切实际的。只有归类为长期支持 (LTS) 的特定版本才享有此待遇。所有版本均适用于生产环境,但大多数企业用户只会选择使用 LTS JDK 部署应用程序。
当前的 Java 版本是 JDK 22,于 2024 年 3 月推出。而 2023 年 9 月发布的 JDK 21 是最新的 LTS 版本,它包含一些值得关注的新功能,并因此对支持大量并行用户的应用程序颇具吸引力。
Java 线程简介
Java 语言从一开始就支持并行执行任务的概念。与 C 和 C++ 等依赖外部库提供并行支持的语言不同,Java 将线程的概念内置到了语言中。假设您正在开发一款将支持许多并行用户的 Web 应用程序。在这种情况下,您可以针对每位用户的连接,分配一个或多个专属线程来处理必要的事务,从而实现对用户连接的并行处理。这一切都将独立完成,确保在所有用户之间实现数据隔离。这被称为一线程一请求 (TPR) 编程模型。
虽然对于此类应用程序的开发人员来说,这是十分有利的功能,但也存在一些限制。例如,Linux 作为广受欢迎的操作系统 (OS),可以处理线程的所有底层问题,并将线程分配至所用硬件的可用 CPU 和内核。在 JDK 21 之前,所有 Java 线程都直接映射到操作系统线程,因此 JVM 不需要处理底层细节。
Java 线程的限制
Java 线程的不足之处在于,当处理数十万(或更多)并行连接时,可扩展性将受到限制。无论是在本地还是云端,当使用如此之多的线程时,对内存的需求使得预配置具有成本效益的服务器硬件变得不切实际。
如果观察大多数 TPR 应用程序的工作方式,就会发现大部分处理过程都涉及调用系统的其他部分,如数据库、文件,以及与其他服务的网络连接。例如,这些连接要求线程在数据库响应之前保持等待(也称为线程阻塞)。实际上,线程大部分时间都在等待,未能积极利用分配给它的操作系统线程。
JDK 21 引入了虚拟线程功能。我们现在可以实现多对一映射,而不是在 Java 线程和操作系统线程之间进行一对一映射。多个 Java 线程将共享一个操作系统线程。
对于开发人员来说,迁移到虚拟线程非常简单,只需要更改线程的创建方式,无需改变使用方式。当应用程序运行时,由 JVM 负责在共享一个操作系统(现称为平台)线程的虚拟线程之间进行切换。当 Java 线程发起可能阻塞的调用时,JVM 会记录线程状态的详细信息,并将平台线程切换到另一个拥有待处理工作的 Java 线程。默认情况下,虚拟线程的内存需求大约是平台线程的千分之一,因此无需增加硬件即可大幅提高可扩展性。
理解可扩展性与性能的区别
在考虑虚拟线程时,理解可扩展性和性能之间的区别至关重要。例如,电子商务应用程序的可扩展性决定了可以同时访问该应用程序的用户数量。而应用程序的性能则决定了系统响应每个请求时的速度。虚拟线程有可能提升应用程序的可扩展性,支持同时处理更多连接。但它无法更快地将结果传递给这些连接。此外,还应该注意应用程序处理每个连接的方式。如果连接涉及 CPU 密集型任务,而非大部分时间都在等待的任务,通常会导致可扩展性变差,连接因无法访问共享的平台线程而超时。
要为您的应用程序提供更出色的可扩展性和性能,不妨考虑使用不同的JVM,例如 Azul Platform Prime。
Java 虚拟线程和高性能 JVM
Platform Prime 的 JDK 21 版本不仅包含了虚拟线程,还采用了不同的即时 (JIT) 编译器。与 OpenJDK HotSpot C2 JIT 相比,Falcon JIT 编译器可以生成优化程度更高的代码,从而提高性能和可扩展性。Platform Prime 完全符合 Java SE 规范,并通过了所有必需的 TCK 测试,因此可无缝替换其他 JDK。您无需重写甚至重新编译任何代码。
不妨免费试用一下 Platform Prime JDK 21 的虚拟线程,看看应用程序在可扩展性和性能方面的改进。