1. 简介
在我们的应用程序中,我们经常需要能够同时执行多项操作。我们可以通过多种方式实现这一点,但其中最关键的是以某种形式实现多任务处理。
多任务意味着同时运行多个任务,每个任务都在执行其工作。这些任务通常同时运行,读取和写入相同的内存并与相同的资源交互,但执行不同的操作。
2. 原生线程
Java中实现多任务的标准方式是使用线程,线程通常由操作系统支持,我们将在此级别工作的线程称为“本机线程”。
操作系统具有一些线程处理能力,而我们的应用程序通常无法使用这些能力,这仅仅是因为它与底层硬件的距离更近。这意味着执行本机线程通常效率更高,这些线程直接映射到计算机CPU上的执行线程-操作系统管理线程到CPU核心的映射。
Java中的标准线程模型(涵盖所有JVM语言)使用本机线程,自Java 1.2以来一直如此,无论JVM运行在什么底层系统上都是如此。
这意味着,任何时候我们使用Java中的任何标准线程机制,我们都在使用本机线程。这包括java.lang.Thread、java.util.concurrent.Executor、java.util.concurrent.ExecutorService等等。
3. 绿色线程
在软件工程中,原生线程的一个替代方案是绿色线程。我们使用线程,但它们并不直接映射到操作系统线程。相反,底层架构管理线程本身,并管理如何将它们映射到操作系统线程。
通常,这是通过运行多个本机线程,然后将绿色线程分配给这些本机线程来执行来实现的。然后,系统可以选择在任意给定时间哪些绿色线程处于活动状态,以及它们在哪些本机线程上处于活动状态。
这听起来很复杂,事实也确实如此。但我们通常不需要关心这种复杂情况,底层架构会处理所有这些问题,我们可以像使用本机线程模型一样使用它。
那么我们为什么要这样做呢?本机线程运行起来非常高效,但启动和停止它们的成本很高。绿色线程有助于避免这种成本,并为架构提供更大的灵活性。如果我们使用相对较长时间运行的线程,那么本机线程非常高效。对于非常短暂的作业,启动它们的成本可能超过使用它们的好处。在这些情况下,绿色线程可以变得更高效。
不幸的是,Java没有内置对绿色线程的支持。
早期版本使用绿色线程而非原生线程作为标准线程模型,这种情况在Java 1.2中发生了改变,自此以后JVM级别不再支持绿色线程。
在库中实现绿色线程也具有挑战性,因为它们需要非常低级别的支持才能表现良好。因此,一种常见的替代方案是使用纤程。
4. 纤程
纤程是多线程的另一种形式,与绿色线程类似。在这两种情况下,我们都不使用本机线程,而是使用随时运行的底层系统控件。绿色线程和纤程之间的最大区别在于控制级别,具体来说是谁在控制。
绿色线程是一种抢占式多任务处理,这意味着底层架构完全负责决定在给定时间执行哪些线程。
这意味着所有常见的线程问题都适用,我们不知道线程执行的顺序,也不知道哪些线程会同时执行。这还意味着底层系统需要能够随时暂停和重新启动我们的代码,可能在方法中间,甚至在语句中间。
相反,纤程是一种协作式多任务处理形式,这意味着正在运行的线程将继续运行,直到它发出信号表示可以交由另一个线程执行。这意味着我们有责任让纤程相互协作,使我们能够直接控制纤程何时可以暂停执行,而不是由系统为我们决定。
这也意味着我们需要以允许这种做法的方式编写代码,否则,它将无法工作。如果我们的代码没有任何中断点,那么我们可能根本不使用纤程。
Java目前没有内置对纤程的支持,不过有些库可以将其引入到我们的应用程序中,包括但不限于:
4.1 Quasar
Quasar是一个Java库,可以与纯Java和Kotlin很好地配合使用,并且有一个可与Clojure配合使用的替代版本。
它的工作原理是让一个需要与应用程序一起运行的Java代理,该代理负责管理Fiber并确保它们正确地协同工作。使用Java代理意味着不需要特殊的构建步骤。
Quasar还需要Java 11才能正常工作,因此可能会限制可以使用它的应用程序。旧版本可以在Java 8上使用,但这些版本不受积极支持。
4.2 Kilim
Kilim是一个Java库,它提供与Quasar非常相似的功能,但使用字节码织入而不是Java代理来实现。这意味着它可以在更多场景使用,但它使构建过程更加复杂。
Kilim适用于Java 7及更新版本,即使在无法使用Java代理的情况下也能正常工作。例如,如果已经使用其他代理进行检测或监控。
4.3 Project Loom
Project Loom是OpenJDK的一个实验项目,旨在将纤程添加到JVM本身,而不是作为附加库。这将使我们获得纤程相对于线程的优势。通过直接在JVM上实现它,它可以帮助避免Java代理和字节码织入带来的复杂性。
Project Loom目前没有发布时间表,但我们现在可以下载早期访问二进制文件来了解进展情况。然而,由于现在还处于早期阶段,我们需要谨慎地依赖它来编写任何生产代码。
5. Coroutines
Coroutines是线程和纤程的替代方案,我们可以将Coroutines视为没有任何形式调度的纤程。无需由底层系统决定在任何时候执行哪些任务,我们的代码直接执行此操作。
通常,我们编写Coroutines时,它们会在流程的特定点产生结果。这些可以看作是我们函数中的暂停点,函数会在此停止工作并可能输出一些中间结果。当我们产生结果时,函数就会停止,直到调用代码出于某种原因决定重新启动,这意味着我们的调用代码控制着何时运行的调度。
Kotlin在其标准库中内置了对Coroutines的原生支持。如果需要,我们还可以使用其他几个Java库来实现它们。
6. 总结
我们在代码中看到了多种不同的多任务替代方案,从传统的本机线程到一些非常轻量级的替代方案。
Post Directory
