1. Introduction
1.介绍
Often in our applications, we need to be able to do multiple things at the same time. We can achieve this in several ways, but key amongst them is to implement multitasking in some form.
在我们的应用程序中,我们经常需要能够同时做多件事情。我们可以通过多种方式实现这一点,但其中的关键是以某种形式实现多任务。
Multi-tasking means running multiple tasks at the same time, where each task is performing its work. These tasks typically all run at the same time, reading and writing the same memory and interacting with the same resources, but doing different things.
多任务是指同时运行多个任务,每个任务都在执行其工作。这些任务通常都在同一时间运行,读写相同的内存,与相同的资源互动,但做着不同的事情。
2. Native Threads
2.本地线程
The standard way of implementing multi-tasking in Java is to use threads. Threading is usually supported down to the operating system. We call threads that work at this level “native threads”.
在Java中实现多任务的标准方式是使用线程。线程通常被支持到操作系统中。我们把在这一层次工作的线程称为 “本地线程”。
The operating system has some abilities with threading that are often unavailable to our applications, simply because of how much closer it is to the underlying hardware. This means that executing native threads are typically more efficient. These threads directly map to threads of execution on the computer CPU – and the operating system manages the mapping of threads onto CPU cores.
操作系统在线程方面有一些能力,而这些能力往往是我们的应用程序所不具备的,仅仅是因为它与底层硬件的关系更为密切。这意味着,执行本地线程通常更有效率。这些线程直接映射到计算机CPU上的执行线程–操作系统管理着线程在CPU内核上的映射。
The standard threading model in Java, covering all JVM languages, uses native threads. This has been the case since Java 1.2 and is the case regardless of the underlying system that the JVM is running on.
Java中的标准线程模型(涵盖所有JVM语言)使用本地线程。自Java 1.2以来,情况一直如此,无论JVM运行在什么底层系统上,都是如此。
This means that any time we use any of the standard threading mechanisms in Java, then we’re using native threads. This includes java.lang.Thread, java.util.concurrent.Executor, java.util.concurrent.ExecutorService, and so on.
这意味着,任何时候我们使用Java中的任何标准线程机制,那么我们就在使用本地线程。这包括java.lang.Thread、java.util.concurrent.Executor、java.util.concurrent.ExecutorService,等等。
3. Green Threads
3.绿色的线
Typically this works by running several native threads and then allocating the green threads onto these native threads for execution. The system can then choose which green threads are active at any given time, and which native threads they are active on.
典型的做法是运行几个本地线程,然后将绿色线程分配到这些本地线程上执行。然后,系统可以选择哪些绿色线程在任何给定的时间是活跃的,以及它们在哪些本地线程上活跃。
This sounds very complicated, and it is. But it’s a complication that we generally don’t need to care about. The underlying architecture takes care of all of this, and we get to use it as if it was a native threading model.
这听起来非常复杂,而且确实如此。但这是一个我们通常不需要关心的复杂问题。底层架构处理了所有这些,我们可以像使用本地线程模型一样使用它。
So why would we do this? Native threads are very efficient to run, but they have a high cost around starting and stopping them. Green threads help to avoid this cost and give the architecture a lot more flexibility. If we are using relatively long-running threads, then native threads are very efficient. For very short-lived jobs, the cost of starting them can outweigh the benefit of using them. In these cases, green threads can become more efficient.
那么,我们为什么要这样做呢?原生线程的运行效率很高,但它们在启动和停止时有很高的成本。绿色线程有助于避免这种成本,并给架构带来更多的灵活性。如果我们使用的是运行时间相对较长的线程,那么本机线程就非常高效。对于寿命很短的作业,启动它们的成本可能超过使用它们的好处。在这些情况下,绿色线程可以变得更有效率。
Unfortunately, Java does not have built-in support for green threads.
不幸的是,Java没有对绿色线程的内置支持。
Very early versions used green threads instead of native threads as the standard threading model. This changed in Java 1.2, and there has not been any support for it at the JVM level since.
非常早期的版本使用绿色线程而不是本地线程作为标准线程模型。这在Java 1.2中发生了变化,此后在JVM层面上就没有任何支持了。
It’s also challenging to implement green threads in libraries because they would need very low-level support to perform well. As such, a common alternative used is fibers.
在库中实现绿色线程也很有挑战性,因为它们需要非常低级的支持才能表现良好。因此,一个常用的替代方案是纤维。
4. Fibers
4.纤维
Fibers are an alternative form of multi-threading and are similar to green threads. In both cases, we aren’t using native threads and instead are using the underlying system controls which are running at any time. The big difference between green threads and fibers is in the level of control, and specifically who is in control.
纤维是多线程的另一种形式,类似于绿色线程。在这两种情况下,我们都没有使用原生线程,而是使用底层的系统控件,这些控件在任何时候都在运行。绿线程和纤维的最大区别在于控制水平,特别是谁在控制。
Green threads are a form of preemptive multitasking. This means that the underlying architecture is entirely responsible for deciding which threads are executing at any given time.
绿线程是一种抢占式多任务的形式。这意味着底层架构完全负责决定在任何特定时间执行哪些线程。
This means that all of the usual issues of threading apply, where we don’t know anything about the order of our threads executing, or which ones will be executing at the same time. It also means that the underlying system needs to be able to pause and restart our code at any time, potentially in the middle of a method or even a statement.
这意味着线程的所有常规问题都适用,我们对线程的执行顺序一无所知,也不知道哪些线程会在同一时间执行。这也意味着底层系统需要能够在任何时候暂停和重新启动我们的代码,可能是在一个方法甚至一个语句的中间。
Fibers are instead a form of cooperative multitasking, meaning that a running thread will continue to run until it signals that it can yield to another. It means that it is our responsibility for the fibers to co-operate with each other. This puts us in direct control over when the fibers can pause execution, instead of the system deciding this for us.
纤维反而是一种合作多任务的形式,意味着一个正在运行的线程将继续运行,直到它发出信号说它可以让给另一个线程。这意味着我们有责任让纤维之间相互合作。这使得我们可以直接控制纤维何时可以暂停执行,而不是由系统替我们决定。
This also means we need to write our code in a way that allows for this. Otherwise, it won’t work. If our code doesn’t have any interruption points, then we might as well not be using fibers at all.
这也意味着我们需要以一种允许这样做的方式来编写我们的代码。否则,它将无法工作。如果我们的代码没有任何中断点,那么我们还不如根本不使用纤维。
Java does not currently have built-in support for fibers. Some libraries exist that can introduce this to our applications, including but not limited to:
Java目前没有对纤维的内置支持。存在一些可以将其引入我们的应用程序的库,包括但不限于:。
4.1. Quasar
4.1.Quasar
Quasar is a Java library that works well with pure Java and Kotlin and has an alternative version that works with Clojure.
Quasar是一个Java库,可以很好地与纯Java和Kotlin一起使用,并且有一个可以与Clojure一起使用的替代版本。
It works by having a Java agent that needs to run alongside the application, and this agent is responsible for managing the fibers and ensuring that they work together correctly. The use of a Java agent means that there are no special build steps needed.
它的工作原理是有一个需要与应用程序一起运行的Java代理,这个代理负责管理纤维并确保它们能正确地一起工作。使用一个Java代理意味着不需要特殊的构建步骤。
Quasar also requires Java 11 to work correctly so that might limit the applications that can use it. Older versions can be used on Java 8, but these are not actively supported.
Quasar还需要Java 11才能正常工作,所以这可能会限制可以使用它的应用程序。较早的版本可以在Java 8上使用,但这些版本不被积极支持。
4.2. Kilim
4.2.Kilim
Kilim is a Java library that offers very similar functionality to Quasar but does so by using bytecode weaving instead of a Java agent. This means that it can work in more places, but it makes the build process more complicated.
Kilim是一个Java库,它提供了与Quasar非常相似的功能,但通过使用字节码编织而不是Java代理来实现的。这意味着它可以在更多的地方工作,但它使构建过程更加复杂。
Kilim works with Java 7 and newer and will work correctly even in scenarios where a Java agent is not an option. For example, if a different one is already used for instrumentation or monitoring.
Kilim适用于Java 7和更新的版本,即使在不能选择Java代理的情况下也能正确工作。例如,如果一个不同的代理已经被用于仪表或监控。
4.3. Project Loom
4.3.织布机项目
Project Loom is an experiment by the OpenJDK project to add fibers to the JVM itself, rather than as an add-on library. This will give us the advantages of fibers over threads. By implementing it on the JVM directly, it can help to avoid complications that Java agents and bytecode weaving introduce.
Project Loom是OpenJDK项目的一项实验,旨在将纤维添加到JVM本身,而不是作为一个附加库。这将为我们提供纤维相对于线程的优势。通过直接在JVM上实现它,它可以帮助避免Java代理和字节码编织所带来的复杂问题。
There is no current release schedule for Project Loom, but we can download early access binaries right now to see how things are going. However, because it is still very early, we need to be careful relying on this for any production code.
目前没有Project Loom的发布时间表,但我们现在可以下载早期访问的二进制文件,看看事情进展如何。然而,由于它仍然是非常早期的,我们需要谨慎地将其作为任何生产代码的依据。
5. Co-Routines
5.共同程序
Co-routines are an alternative to threading and fibers. We can think of co-routines as fibers without any form of scheduling. Instead of the underlying system deciding which tasks are performing at any time, our code does this directly.
协同程序是线程和纤维的替代品。我们可以将联合程序视为没有任何形式的调度的纤维。我们的代码不是由底层系统来决定在任何时候执行哪些任务,而是直接做这个。
Generally, we write co-routines so that they yield at specific points of their flow. These can be seen as pause points in our function, where it will stop working and potentially output some intermediate result. When we do yield, we are then stopped until the calling code decides to re-start us for whatever reason. This means that our calling code controls the scheduling of when this will run.
一般来说,我们在编写联合程序时,会让它们在其流程的特定点上产生。这些可以被看作是我们函数的暂停点,在那里它将停止工作并可能输出一些中间结果。当我们屈服时,我们就会停止工作,直到调用代码出于某种原因决定重新启动我们。这意味着我们的调用代码控制着何时运行的调度。
Kotlin has native support for co-routines built into its standard library. There are several other Java libraries that we can use to implement them as well if desired.
Kotlin在其标准库中内置了对联合程序的本地支持。如果需要的话,我们也可以用其他几个Java库来实现它们。
6. Conclusion
6.结论
We’ve seen several different alternatives for multi-tasking in our code, ranging from the traditional native threads to some very light-weight alternatives. Why not try them out next time an application needs concurrency?
我们已经在代码中看到了几种不同的多任务替代方案,从传统的本地线程到一些非常轻量级的替代方案。为什么不在下一次应用程序需要并发的时候试试它们呢?