1. Java线程模型和虚线程
1.1 平台线程
在 Java 中,经典线程是 java.lang.Thread 类的实例。后面我们也将它们称为平台线程。
传统上,Java 将平台线程视为围绕操作系统 (OS) 线程的瘦包装器。创建这样的平台线程一直很昂贵(由于操作系统维护的堆栈和其他资源很大),因此 Java 一直使用线程池来避免线程创建的开销。
平台线程的数量也必须受到限制,因为这些非常消耗资源的线程会影响整个机器的性能,这主要是因为平台线程被 1:1 映射到 OS 线程。通常,在CPU,网络连接等成为系统瓶颈前,相当数量的平台线程会首先成为系统的瓶颈。
换句话说,在硬件资源稍有冗余的前提下,平台线程首先成为了系统吞吐量的瓶颈。
1.2 平台线程的可扩展性问题
平台线程一直很容易建模、编程和调试,因为它们使用平台的并发单元来表示应用程序的并发单元。它被称为一个线程一个请求的模式。
但是这种模式限制了服务器的吞吐量,因为并发请求的数量(服务器可以处理)与服务器的硬件性能成正比。因此,即使在多核处理器中,可用线程的数量也必须受到限制。
除了线程数量之外,延迟也是一个大问题。如果我们仔细观察,在当今的微服务世界中,请求是通过在多个系统和服务器上获取/更新数据来服务的。在应用程序等待来自其他服务器的信息时,当前平台线程保持在空闲状态。这是对计算资源的浪费,也是实现高吞吐量应用程序的主要障碍。
1.3 反应式编程 (Reactive Programming) 的问题
反应式编程解决了平台线程等待其他系统响应的问题。异步 API 不等待响应,而是通过回调工作。每当线程调用异步 API 时,平台线程都会返回到池中,直到响应从远程系统或数据库返回。稍后,当响应到达时,JVM 将从池中分配另一个线程来处理响应,依此类推。这样,多个线程参与处理单个异步请求。
在异步编程中,延迟被消除了,但由于硬件限制,平台线程的数量仍然有限,因此我们对可扩展性有限制。另一个大问题是这样的异步程序在不同的线程中执行,因此很难调试或分析它们。
此外,我们必须采用一种新的编程风格,远离典型的循环和条件语句。新的 lambda 样式语法使得理解现有代码和编写程序变得困难,因为我们现在必须将程序分解为多个可以独立和异步运行的代码块。
所以我们可以说,虚拟线程还通过适应传统语法来提高代码质量,同时具有反应式编程的好处。