Java 21 神仙特性:虚拟线程使用指南

虚拟线程是由 JAVA 21 版本中实现的一种轻量级线程 。它由 JVM 进行创建以及管理 。虚拟线程和传统线程(我们称之为平台线程)之间的主要区别在于,我们可以轻松地在一个 Java 程序中运行大量、甚至数百万个虚拟线程 。
由于虚拟线程的数量众多,也就赋予了 Java 程序强大的力量 。虚拟线程适合用来处理大量请求 , 它们可以更有效地运行 “一个请求一个线程” 模型编写的 web 应用程序,可以提高吞吐量以及减少硬件浪费 。
由于虚拟线程是 java.lang.Thread 的实现,并且遵守自 Java SE 1.0 以来指定 java.lang.Thread 的相同规则,因此开发人员无需学习新概念即可使用它们 。
但是虚拟线程才刚出来,对我们来说有一些陌生 。由于 Java 历来版本中无法生成大量平台线程(多年来 Java 中唯一可用的线程实现),已经让程序员养成了一套关于平台线程的使用习惯 。这些习惯做法在应用于虚拟线程时会适得其反 , 我们需要摒弃 。
此外虚拟线程和平台线程在创建成本上的巨大差异,也提供了一种新的关于线程使用的方式 。Java 的设计者鼓励使用虚拟线程而不必担心虚拟线程的创建成本 。
本文无意全面涵盖虚拟线程的每个重要细节,目的只是提供一套介绍性指南,以帮助那些希望开始使用虚拟线程的人充分利用它们 。
关于更多有关虚拟线程和平台线程的介绍 , 大家可以看我《3 分钟理解 Java 虚拟线程》这篇文章有详细讲解 。
本文完整大纲如下,

Java 21 神仙特性:虚拟线程使用指南

文章插图
图片
请大方使用同步阻塞 IO【Java 21 神仙特性:虚拟线程使用指南】虚拟线程可以显着提高以 “一个请求一个线程” 模型编写的 web 应用程序的吞吐量(注意不是延迟) 。在这种模型中,web 应用程序针对每个客户端请求都会创建一个线程进行处理 。因此为了处理更多的客户端请求,我们需要创建更多的线程 。
在 “一个请求一个线程” 模型中使用平台线程的成本很高,因为平台线程与操作系统线程对应(操作系统线程是一种相对稀缺的资源),阻塞了平台线程 , 会让它无事可做一直处于阻塞中,这样就会造成很大的资源浪费 。
然而 , 在这个模型中使用虚拟线程就很合适,因为虚拟线程非常廉价就算被阻塞也不会造成资源浪费 。因此在虚拟线程出来后,Java 的设计者是建议我们应该以简单的同步风格编写代码并使用阻塞 IO 。
举个例子,以下用非阻塞异步风格编写的代码是不会从虚拟线程中受益太多的,
CompletableFuture.supplyAsync(info::getUrl, pool).thenCompose(url -> getBodyAsync(url, HttpResponse.BodyHandlers.ofString())).thenApply(info::findImage).thenCompose(url -> getBodyAsync(url, HttpResponse.BodyHandlers.ofByteArray())).thenApply(info::setImageData).thenAccept(this::process).exceptionally(t -> { t.printStackTrace(); return null; });另一方面,以下用同步风格并使用阻塞 IO 编写的代码使用虚拟线程将受益匪浅,
try {String page = getBody(info.getUrl(), HttpResponse.BodyHandlers.ofString());String imageUrl = info.findImage(page);byte[] data = https://www.isolves.com/it/cxkf/yy/JAVA/2023-12-28/getBody(imageUrl, HttpResponse.BodyHandlers.ofByteArray());info.setImageData(data);process(info);} catch (Exception ex) {t.printStackTrace();}并且上面的同步代码也更容易在调试器中调试、在分析器中分析或通过线程转储进行观察 。要观察虚拟线程 , 可以使用 jcmd 命令创建线程转储,
jcmd <pid> Thread.dump_to_file -format=json <file>用同步风格并使用阻塞 IO 风格编写的代码越多,虚拟线程的性能和可观察性就越好 。而用异步非阻塞 IO 风格编写的程序或框架,如果每个任务没有专用一个线程,则无法从虚拟线程中获得显着的好处 。
使用虚拟线程,我们因该避免将同步阻塞 IO 与异步非阻塞 IO 混为一谈 。
避免池化虚拟线程关于虚拟线程使用方面最难理解的一件事情就是 , 我们不应该池化虚拟线程 。虽然虚拟线程具有与平台线程相同的行为,但虚拟线程和线程池其实是两种概念 。
平台线程是一种稀缺资源,因为它很宝贵 。越宝贵的资源就越需要管理,管理平台线程最常见的方法是使用线程池 。
不过在使用线程池后,我们需要回答的一个问题 , 线程池中应该有多少个线程?最小线程数、最大线程数应该设置多少?这也是一个问题 。


推荐阅读