Project Loom引入了被称为纤维的轻量级和高效的虚拟线程,大规模提高了资源效率,同时为开发者保留了同样简单的线程抽象。

思想库
Loom是Java/JVM生态系统中一个较新的项目(由OpenJDK主持),它试图解决传统并发模型中的限制。特别是,Loom提供了一个更轻的线程替代方案,以及用于管理线程的新语言结构。
继续阅读,了解这些即将到来的重要变化。
Fibers。Java中的虚拟线程
传统的Java并发是用Thread和Runnable类来管理的,如清单1所示(启动一个新的命名线程并输出名称)。
清单1.用传统的Java启动一个线程
Thread thread = new Thread("My Thread") {
这种模式在简单的情况下相当容易理解,而且Java提供了大量的支持来处理它。
缺点是,Java线程直接被映射到操作系统中的线程。这对并发的Java应用程序的可扩展性造成了硬性限制。它不仅意味着应用程序线程和操作系统线程之间是一对一的关系,而且没有机制来组织线程以实现最佳安排。例如,密切相关的线程可能最终共享不同的进程,而它们可以从共享同一进程的堆中受益。
为了让你了解Loom中的变化有多大,目前的Java线程,即使有庞大的服务器,也是数以千计的线程(最多是)。Loom提议将这一限制移至百万线程。这对Java服务器可扩展性的影响是惊人的,因为标准的请求处理是与线程数相联系的。
解决方案是引入某种虚拟线程,将Java线程从底层操作系统线程中抽象出来,JVM可以更有效地管理两者之间的关系。这就是Project Loom所要做的,它引入了一个新的虚拟线程类,称为纤维。
正如Project Loom提案中所说:
实现连续性的主要技术任务--实际上也是整个项目的主要技术任务--是为HotSpot增加捕获、存储和恢复不属于内核线程的调用栈的能力。
如果你曾经接触过Quasar,它通过字节码操作给Java带来了轻量级的线程,同一个技术负责人(Ron Pressler)为Oracle负责Loom。
Java中纤维的替代品
在更仔细地研究Loom的解决方案之前,应该提到的是,已经有各种方法被提议用于并发处理。一般来说,这些方法相当于异步编程模型。一些方法,如CompletableFutures和Non-Blocking IO,通过提高线程的使用效率,在事物的边缘工作。其他的,如JavaRX(ReactiveX规范的Java实现),是完全的异步替代品。
尽管JavaRX是一种强大的、潜在的高性能的并发方法,但它也不是没有缺点的。特别是,它与Java开发人员传统上使用的现有心理结构完全不同。另外,JavaRX不能与在虚拟机层管理虚拟线程所能达到的理论性能相提并论。
纤维被设计成允许类似于JavaScript的async/await的同步代码流,同时在JVM中隐藏了许多影响性能的中间件。
正在运行的Java纤维
如前所述,新的Fiber类代表一个虚拟线程。在引擎盖下,异步杂技正在进行中。为什么要这么麻烦,而不是在语言层面上采用类似ReactiveX的东西?答案是,既要让开发者更容易理解,又要让现有的代码宇宙更容易移动。例如,数据存储驱动可以更容易地过渡到新的模型。
在清单2中可以看到一个使用纤维的非常简单的例子。注意它与现有的Thread代码非常相似。(这个片段来自于Oracle的博客文章)。
清单2.创建一个虚拟线程
Thread.startVirtualThread(
除了这个非常简单的例子之外,还有很多关于调度的考虑。这些机制还没有定型,而Loom提案对其中的想法给出了一个很好的概述。
关于Loom的纤维的一个重要说明是,无论对整个Java系统需要做什么改变,都不会破坏现有的代码。现有的线程代码将完全兼容前进。你可以使用纤维,但你不一定要使用。你可以想象,这是一个相当艰巨的任务,占了从事Loom工作的人的大部分时间。
低级别的异步与持续器
现在我们已经看到了纤维,让我们来看看延续性。Loom 实现了延续性,以支持纤维,并将纤维作为公共 API 暴露给开发者在应用程序中使用。那么,什么是延续?
在高层次上,延续是执行流程在代码中的一种表示。换句话说,一个延续允许开发者通过调用函数来操纵执行流程。Loom文档提供了清单3中的例子,它提供了一个很好的心理暗示,说明了它是如何工作的。
清单3.延续的例子
foo() { // (2)
考虑一下每个注释数字所描述的执行流程。
(0) 创建一个延续,从foo 函数开始
(1) 将控制权传递给延续的入口点
(2) 执行到下一个暂停点,也就是(3)处
(3) 将控制权释放回起点,即(1)处
(4) 现在执行,在延续中调用continue,流程返回到(5)处暂停的地方
这种控制在像JavaScript这样的语言中并不困难,因为在这种语言中,函数很容易被引用,并且可以随意调用以引导执行流。
消除尾部调用
Loom的另一个目标是消除尾部调用(也称为尾部调用优化)。这是该系统的一个相当深奥的元素。其核心思想是,该系统将能够尽可能地避免为延续分配新的堆栈。在这种情况下,执行连续程序所需的内存量保持一致,而不是不断地增加,因为过程中的每一步都需要保存前一个堆栈,并在调用堆栈解开时使之可用。
Loom和Java的未来
Loom和一般的Java突出地致力于构建网络应用。显然,Java还被用于许多其他领域,而Loom引入的理念很可能在这些应用中发挥作用。很容易看出,大规模提高线程效率,并大幅减少处理多个竞争需求的资源需求,将为服务器带来更大的吞吐量。更好地处理请求和响应是整个宇宙中现有的和将要建立的Java应用的一个底线胜利。
像任何雄心勃勃的新项目一样,Loom也不是没有挑战的。处理复杂的线程交错(虚拟或其他)总是一个复杂的挑战,我们将不得不等待,看看到底出现了哪些库支持和设计模式来处理这些情况。
随着Project Loom进入主分支,并根据现实世界的使用情况进行演化,这将是令人着迷的观察。随着这种情况的出现,以及新系统的固有优势被开发人员所依赖的基础设施所采纳(想想Jetty和Tomcat等Java应用服务器),我们可以看到Java生态系统的巨大变化。
目前,Java和其主要的服务器端竞争对手Node.js在性能上已经不相上下。在典型的网络应用程序使用案例中,如果Java的性能有一个数量级的提升,可能会改变未来几年的局面。