Loom 是 Java/JVM 生态系统(由 OpenJDK 托管)中的一个较新项目,它试图解决传统并发模型中的局限性。特别是,Loom 提供了一种更轻量级的线程替代方案,以及用于管理它们的新语言结构。
继续阅读以了解这些即将发生的重要变化的概述。
传统的 Java 并发性是通过 Thread 和 Runnable 类管理的,如清单 1 所示(它启动一个新的命名线程并输出名称)。
Thread thread = new Thread("My Thread") { public void run(){ System.out.println("run by: " + getName()); } }; thread.start(); System.out.println(thread.getName());
这种模型在简单的情况下相当容易理解,Java 提供了丰富的支持来处理它。
缺点是 Java 线程直接映射到操作系统中的线程。这对并发 Java 应用程序的可扩展性设置了硬性限制。它不仅意味着应用程序线程和操作系统线程之间存在一对一的关系,而且没有组织线程以进行最佳安排的机制。例如,密切相关的线程可能最终共享不同的进程,而它们本可以从共享同一进程的堆中获益。
为了让您了解 Loom 中的变化有多么雄心勃勃,当前的 Java 线程,即使在大型服务器上,也被计入数千个线程(最多)。 Loom 提议将此限制向 百万线程 移动。这对 Java 服务器可伸缩性的影响是惊人的,因为标准请求处理与线程计数结合在一起。
解决方案是引入某种虚拟线程,Java 线程是从底层 OS 线程中抽象出来的,JVM 可以更有效地管理两者之间的关系。这就是 Loom 项目打算做的事情,它引入了一个名为 fiber 的新虚拟线程类。
As the 织机项目提案 states:
实现延续的主要技术任务——事实上,整个项目——是为 HotSpot 添加捕获、存储和恢复调用堆栈的能力,而不是作为内核线程的一部分。
如果您曾经接触过 类星体,它通过字节码操作为 Java 带来了轻量级线程,那么同一位技术主管 (Ron Pressler) 领导着 Loom for Oracle。
在更仔细地研究 Loom 的解决方案之前,应该提到已经提出了多种用于并发处理的方法。通常,这些相当于异步编程模型。有些,如 CompletableFutures 和 Non-Blocking IO,通过提高线程使用效率来解决问题。其他的,如 JavaRX(ReactiveX 规范的 Java 实现),是大规模的异步替代方案。
尽管 JavaRX 是一种功能强大且具有潜在高性能的并发方法,但它并非没有缺点。特别是,它与 Java 开发人员传统上使用的现有思维结构截然不同。此外,JavaRX 无法达到通过在虚拟机层管理虚拟线程可实现的理论性能。
纤程旨在允许类似 JavaScript 的 async/await 的同步出现的代码流,同时隐藏 JVM 中的大部分性能问题中间件。
如前所述,新的 Fiber 类代表一个虚拟线程。在引擎盖下,异步杂技正在进行中。为什么要解决这个问题,而不是仅仅在语言级别采用 ReactiveX 之类的东西?答案是既让开发人员更容易理解,也让移动现有代码更容易。例如,数据存储驱动程序可以更轻松地过渡到新模型。
清单 2 中显示了一个使用纤程的非常简单的示例。注意它与现有的 Thread 代码非常相似。 (此片段来自这篇 Oracle 博客文章。)
Thread.startVirtualThread( () -> { System.out.println("Hello World"); });
除了这个非常简单的例子之外,还有很多关于调度的考虑因素。这些机制还不是一成不变的,织机提案很好地概述了所涉及的想法。
关于 Loom 的纤维的一个重要说明是,无论需要对整个 Java 系统进行什么更改,它们都不会破坏现有代码。现有的线程代码将在未来完全兼容。您可以使用光纤,但不是必须的。可以想象,这是一项相当艰巨的任务,占了 Loom 工作人员花费的大部分时间。
现在我们已经了解了纤维,让我们来看看延续。 Loom 实现延续来支持纤程,并将纤程作为公共 API 公开,供开发人员在应用程序中使用。那么什么是延续呢?
在高层次上,延续是执行流代码的表示。换句话说,continuation 允许开发人员通过调用函数来操纵执行流程。 Loom 文档展示了清单 3 中的示例,从中可以很好地了解其工作原理。
foo() { // (2) ... bar() ...}bar() { ... suspend // (3) ... // (5)}main() { c = continuation(foo) // (0) c.continue() // (1) c.continue() // (4)}
考虑每个注释数字所描述的执行流程:
(0) 创建延续,从 foo
函数开始 (1) 将控制传递到延续的入口点 (2) 执行直到下一个暂停点,即 ( 3) (3) 将控制权释放回原点,在 (1) 处 (4) 现在执行,调用继续继续,流程返回到它在 (5) 处暂停的位置
这种控制在像 JavaScript 这样的语言中是不难的,在这种语言中函数很容易被引用,并且可以随意调用以指导执行流程。
Loom 的另一个既定目标是尾调使用消除(也称为尾调使用优化)。这是所提议系统的一个相当深奥的元素。核心思想是系统将能够尽可能避免为延续分配新的堆栈。在这种情况下,执行延续所需的内存量保持一致,而不是不断构建,因为过程中的每个步骤都需要保存先前的堆栈并在调用堆栈展开时可用。
一般来说,Loom 和 Java 主要致力于构建 Web 应用程序。显然,Java 还被用在了很多其他领域,而 Loom 引入的思想在这些应用中很可能会派上用场。很容易看出线程效率的大幅提高和处理多个竞争需求的资源需求的显着减少将如何提高服务器的吞吐量。更好地处理请求和响应是整个现有和待构建 Java 应用程序领域的底线胜利。
与任何雄心勃勃的新项目一样,Loom 并非没有挑战。处理复杂的线程交错(虚拟或其他)始终是一个复杂的挑战,我们将不得不拭目以待究竟会出现哪些库支持和设计模式来应对这些情况。
随着 Project Loom 进入主要分支并根据实际使用情况不断发展,这将是一件令人着迷的事情。随着这种情况的发生,以及新系统固有的优势被应用到开发人员所依赖的基础架构中(想想 Jetty 和 Tomcat 等 Java 应用程序服务器),我们可以看到 Java 生态系统发生翻天覆地的变化。
Java 及其主要的服务器端竞争对手 Node.js 在性能上已经并驾齐驾。在典型的 Web 应用程序用例中,Java 性能的数量级提升可能会改变未来几年的格局。
地址:https://www.cundage.com/article/3652596-project-loom-understand-the-new-java-concurrency-model.html