您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

搞懂为什么选择 Java 虚拟线程?

时间:2023-11-03 13:58:32  来源:微信公众号  作者:架构驿站

Hello folks,我是 Luga,今天我们来聊一下 JAVA 生态的核心技术—— Java Virtual Threads,即 “Java 虚拟线程” 。

虚拟线程是 Java 中的一个重要创新,在 Project Loom 项目中开发的。自从 Java 19 开始作为预览功能引入,到 Java 21 以后成为正式版本(JEP 444),虚拟线程已经成为 JDK 的一部分。

搞懂为什么选择 Java 虚拟线程?

一、为什么是 Java Virtual Threads ?

众所周知,JVM 是一个多线程环境,通过 java.lang.Thread 类型为我们提供了对操作系统线程的抽象。在 Project Loom 之前,JVM 中的每个线程都只是对操作系统线程的一种简单封装,我们可以称之为“平台线程”。

然而,所谓的“平台线程”,在某些特定的业务场景中,往往存在一些问题,从多个角度来看,它们都是昂贵的。首先,创建平台线程的成本很高。每当创建一个平台线程时,操作系统必须在堆栈中分配大量内存(以兆字节计)来存储线程的上下文、原生调用堆栈和 Java 调用堆栈。由于堆栈大小是固定的,这就导致了高昂的内存开销。此外,每当调度器对线程进行抢占式调度时,也需要移动大量的内存。

因此,我们可以想象,这在空间和时间上都是非常昂贵的操作。实际上,由于堆栈框架的巨大尺寸限制,我们对可创建的线程数量也存在限制。在 Java 中,我们很容易遇到 OutOfMemoryError,只需不断实例化新的平台线程,直到操作系统的内存耗尽为止。

private static void stackOverFlowErrorExample() {
  for (int i = 0; i < 100_000; i++) {
    new Thread(() -> {
      try {
        Thread.sleep(Duration.ofSeconds(1L));
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
    }).start();
  }
}

由于平台线程的创建成本较高,每个线程需要分配一定数量的堆栈内存,因此在某些情况下,如果我们不断实例化新的平台线程,直到操作系统的内存耗尽,就有可能迅速触发 OutOfMemoryError。

然而,这个过程的确切时间取决于多个因素,包括可用的内存大小、操作系统的线程限制以及 JVM 的配置。如果可用的内存较小,同时 JVM 的堆大小也较小,那么在不断实例化新的平台线程时,很可能会很快达到内存的极限,导致 OutOfMemoryError 的发生。

[0.949s][warning][os,thread] FAIled to start thread "Unknown thread" - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 4k, detached.
[0.949s][warning][os,thread] Failed to start the native thread for java.lang.Thread "Thread-4073"
Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached

上述示例展示了我们如何基于当前的受到限制的环境中进行并发编程

然而,Java 自从问世以来一直致力于成为一种简单易用的编程语言。在并发编程领域,我们应该像编写顺序代码一样编写程序。事实上,在 Java 中,为每个并发任务创建一个新线程是编写并发程序更简单的方法之一。这种模型被称为"每个线程一个任务"。

接下来,我们来看一下虚拟线程内部架构,具体如下所示:

搞懂为什么选择 Java 虚拟线程?

使用这种方法,每个线程可以使用自己的局部变量来存储信息,从而大大减少了共享可变状态的需求。线程之间共享状态是并发编程中众所周知的"棘手部分"。然而,通过每个线程一个任务的模型,我们可以轻松地避免复杂的线程同步和共享状态的问题。

然而,正如之前提到的,使用这种方法也存在着限制,即我们能够创建的线程数量有限。由于平台线程的创建成本较高,每个线程都需要分配一定数量的堆栈内存,这限制了我们可以创建的线程数量。如果我们不加限制地创建大量线程,就有可能导致内存耗尽和性能下降。

需要注意的是,随着 Project Loom 的引入,虚拟线程的轻量级特性将显著改善线程创建成本和内存开销。这将使我们能够更轻松地创建大规模的并发任务,而不会受到线程数量限制的困扰。

二、那么,如何创建 Virtual Threads ?

正如我们之前所提到的,虚拟线程是一种新型的线程,旨在解决平台线程的资源限制问题。它们是 java.lang.Thread 类型的替代实现,将堆帧(Heap Frame)存储在堆内存中,而不是堆栈中。

由于虚拟线程的堆栈存储在堆中,因此它们的初始内存占用非常小,通常只有几百字节,而不是兆字节。此外,堆栈块的大小可以动态调整。这意味着我们不需要为每个可能的用例分配数百兆字节的内存。

通常而言,创建一个新的虚拟线程非常简单。我们可以使用 java.lang.Thread类 型上的新工厂方法 ofVirtual 来实现。让我们首先定义一个实用函数,用于创建具有给定名称的虚拟线程的示例代码:

import java.lang.Thread;

public class VirtualThreadExample {
    public static void main(String[] args) {
        Thread virtualThread = Thread.ofVirtual("VirtualThreadExample", VirtualThreadExample::runTask);
        virtualThread.start();
    }
    
    public static void runTask() {
        // 在虚拟线程中执行的任务代码
        System.out.println("Running task in virtual thread");
    }
}

在上面的示例中,我们使用 Thread.ofVirtual 方法创建了一个名为 "VirtualThreadExample" 的虚拟线程,并指定了要在其中执行的任务代码。然后,我们调用 start 方法启动虚拟线程。

通过使用虚拟线程,我们可以更加灵活地管理线程的内存消耗,并提高并发程序的性能和可伸缩性。虚拟线程是 Project Loom 的关键特性之一,将极大地改善 Java 中的并发编程体验。

三、Virtual Threads 到底有哪些方面优势?

作为 Project Loom 提出的一种新的线程模型,即虚拟线程。虚拟线程是一种轻量级的线程,其堆栈存储在堆内存中,而不是在操作系统线程的堆栈中。这种设计使得虚拟线程的创建和销毁成本较低,并且可以创建大量的线程,而不会受到操作系统和硬件资源的限制。

虚拟线程的引入将改变 Java 中的并发编程方式。它们可以通过更高效地利用系统资源来提高并发性能,并且可以简化并发编程的复杂性。虚拟线程可以使用更少的内存,并且可以根据需求动态调整堆栈的大小,以提高资源利用率。

具体可参考如下所示:

1.减少应用程序内存消耗

与传统的由平台线程都映射到操作系统线程的生命周期相对比,虚拟线程通过较小的初始内存占用、动态调整堆栈大小、共享堆栈和更高效的内存管理等方式,减少了应用程序的内存消耗。这使得可以创建更多的线程,提高并发性能,并且更有效地利用系统资源。

2.提高应用程序吞吐量

在大多数架构中,应用程序可以处理的请求数量与应用程序服务器线程池中可用的线程数量成正比。因为每个客户请求都由单个唯一的线程处理。因此,如果可用的线程数量较少,则只能同时处理少量请求。这将降低应用程序的吞吐量。另一方面,如果应用程序服务器线程池配置了Java虚拟线程,它可以创建明显更高的线程数量(数百万),这将最终提高应用程序的吞吐量。

此外,在某些应用程序中,应用程序服务器线程池中的可用线程在其他计算资源(如CPU、内存、网络、存储)饱和之前首先饱和。对于这样的虚拟线程来说,这将是一个较大的增强。

3.减少无法创建新的本机线程的 “OutOfMemoryError” 异常

在 JVM 上运行的应用程序容易出现“java.lang.OutOfMemoryError:无法创建新的本机线程”。这种类型的内存错误通常发生在如下两种情况下:

  • 当应用程序创建的线程超过服务器(或容器)的 RAM 容量时
  • 当应用程序创建的线程超过操作系统允许的限制时(注:在操作系统中,有一个内核限制,该限制规定了单个进程可以创建的线程数量)。

通常而言,Java 虚拟线程在减少内存消耗方面具有显著优势。相比传统的平台线程,Java 虚拟线程通常更轻量级,它们占用的内存较少。这使得使用虚拟线程比使用平台线程更难达到 RAM 容量的饱和。

传统的平台线程需要分配操作系统线程,并且每个线程都有一定的内存开销。而虚拟线程在不做实际工作时,并不需要分配操作系统线程,因此虚拟线程应用程序超过操作系统线程限制的可能性要远远高于传统的平台线程。

虚拟线程的轻量级特性和更高的灵活性使得可以创建更多的线程,而不会受到操作系统和硬件资源的限制。这进一步增加了虚拟线程应用程序处理大规模并发的能力,提高了系统的可伸缩性。

4.提高应用程序可用性

在我们主流的系统架构中,应用程序通常需要与多个后端系统进行通信,如 API、数据库和第三方框架等。然而,当其中一个后端系统出现中断或响应缓慢时,传统的应用程序服务器线程会被阻塞,等待后端系统的响应。随着更多请求进入应用程序,越来越多的线程会被阻塞。在这种情况下,应用程序服务器线程池中的线程数量是有限的。如果所有线程都被阻塞等待后端系统的响应,那么就没有可用线程来处理新的请求,从而导致整个应用程序不可用。

然而,通过将应用程序服务器线程池配置为使用 Java 虚拟线程,可以解决上述问题并提高应用程序的可用性。使用虚拟线程,我们甚至可以轻松创建数百万个线程,而不会出现重大问题。当虚拟线程被阻塞等待后端系统的响应时,它会像任何其他应用程序对象一样,以非常轻量级的方式存储在 Java 堆区域中。因此,应用程序服务器线程池可以继续创建虚拟线程,而不会耗尽线程池中的线程资源,直到后端系统恢复。

这种优化策略为应用程序带来了巨大的潜力,提高了应用程序的可用性。即使在后端系统出现问题时,应用程序仍然能够继续创建和处理请求,而不会因为线程资源的耗尽而导致不可用状态。这种灵活性和弹性使得应用程序能够更好地应对高负载和故障情况,保持稳定的运行状态。

Java 虚拟线程提供了现代应用程序所需的强大且高效的并发模型。它简化了并发编程,并带来更好的资源利用率,因此有可能彻底改变开发人员在 Java 中处理并发代码的方式。

随着 Java 技术不断发展和创新,了解最新的功能如虚拟线程对于那些希望保持领先地位并充分利用 Java 生态系统潜力的开发人员来说至关重要。

虚拟线程提供了一种轻量级的线程模型,通过协作调度和高效的内存管理,大大减少了线程创建和管理的开销。这使得开发人员能够更容易地编写高性能、高并发的应用程序,而无需担心传统线程模型的限制和开销。

通过使用虚拟线程,开发人员可以更好地利用系统资源,提高应用程序的并发性能。虚拟线程的出现为 Java 生态系统带来了更多的潜力和机会,使得开发人员能够更好地应对现代应用程序中的并发需求。

因此,对于那些希望保持领先并充分利用 Java 生态系统的开发人员来说,了解虚拟线程等先进功能是至关重要的。这将使他们能够更好地应对并发编程挑战,并构建出高性能、可扩展的应用程序,从而在竞争激烈的软件开发市场中脱颖而出。



Tags:Java   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  Search: Java  点击:(4)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践&mdash;&mdash;如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  Search: Java  点击:(25)  评论:(0)  加入收藏
Oracle正式发布Java 22
Oracle 正式发布 Java 22,这是备受欢迎的编程语言和开发平台推出的全新版本。Java 22 (Oracle JDK 22) 在性能、稳定性和安全性方面进行了数千种改进,包括对Java 语言、其API...【详细内容】
2024-03-21  Search: Java  点击:(10)  评论:(0)  加入收藏
构建一个通用灵活的JavaScript插件系统?看完你也会!
在软件开发中,插件系统为应用程序提供了巨大的灵活性和可扩展性。它们允许开发者在不修改核心代码的情况下扩展和定制应用程序的功能。本文将详细介绍如何构建一个灵活的Java...【详细内容】
2024-03-20  Search: Java  点击:(20)  评论:(0)  加入收藏
Java 8 内存管理原理解析及内存故障排查实践
本文介绍Java8虚拟机的内存区域划分、内存垃圾回收工作原理解析、虚拟机内存分配配置,以及各垃圾收集器优缺点及场景应用、实践内存故障场景排查诊断,方便读者面临内存故障时...【详细内容】
2024-03-20  Search: Java  点击:(14)  评论:(0)  加入收藏
如何编写高性能的Java代码
作者 | 波哥审校 | 重楼在当今软件开发领域,编写高性能的Java代码是至关重要的。Java作为一种流行的编程语言,拥有强大的生态系统和丰富的工具链,但是要写出性能优异的Java代码...【详细内容】
2024-03-20  Search: Java  点击:(20)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18  Search: Java  点击:(24)  评论:(0)  加入收藏
对JavaScript代码压缩有什么好处?
对JavaScript代码进行压缩主要带来以下好处: 减小文件大小:通过移除代码中的空白符、换行符、注释,以及缩短变量名等方式,可以显著减小JavaScript文件的大小。这有助于减少网页...【详细内容】
2024-03-13  Search: Java  点击:(2)  评论:(0)  加入收藏
跨端轻量JavaScript引擎的实现与探索
一、JavaScript 1.JavaScript语言JavaScript是ECMAScript的实现,由ECMA 39(欧洲计算机制造商协会39号技术委员会)负责制定ECMAScript标准。ECMAScript发展史: 2.JavaScript...【详细内容】
2024-03-12  Search: Java  点击:(2)  评论:(0)  加入收藏
面向AI工程的五大JavaScript工具
令许多人惊讶的是,一向在Web开发领域中大放异彩的JavaScript在开发使用大语言模型(LLM)的应用程序方面同样大有价值。我们在本文中将介绍面向AI工程的五大工具,并为希望将LLM...【详细内容】
2024-02-06  Search: Java  点击:(52)  评论:(0)  加入收藏
▌简易百科推荐
Java 8 内存管理原理解析及内存故障排查实践
本文介绍Java8虚拟机的内存区域划分、内存垃圾回收工作原理解析、虚拟机内存分配配置,以及各垃圾收集器优缺点及场景应用、实践内存故障场景排查诊断,方便读者面临内存故障时...【详细内容】
2024-03-20  vivo互联网技术    Tags:Java 8   点击:(14)  评论:(0)  加入收藏
如何编写高性能的Java代码
作者 | 波哥审校 | 重楼在当今软件开发领域,编写高性能的Java代码是至关重要的。Java作为一种流行的编程语言,拥有强大的生态系统和丰富的工具链,但是要写出性能优异的Java代码...【详细内容】
2024-03-20    51CTO  Tags:Java代码   点击:(20)  评论:(0)  加入收藏
在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述
译者 | 李睿审校 | 重楼在Java开发领域,优化应用程序的性能是开发人员的持续追求。配置文件引导优化(Profile-Guided Optimization,PGO)是一种功能强大的技术,能够显著地提高Ja...【详细内容】
2024-03-18    51CTO  Tags:Java   点击:(24)  评论:(0)  加入收藏
Java生产环境下性能监控与调优详解
堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,...【详细内容】
2024-02-04  大雷家吃饭    Tags:Java   点击:(56)  评论:(0)  加入收藏
在项目中如何避免和解决Java内存泄漏问题
在Java中,内存泄漏通常指的是程序中存在一些不再使用的对象或数据结构仍然保持对内存的引用,从而导致这些对象无法被垃圾回收器回收,最终导致内存占用不断增加,进而影响程序的性...【详细内容】
2024-02-01  编程技术汇  今日头条  Tags:Java   点击:(68)  评论:(0)  加入收藏
Java中的缓存技术及其使用场景
Java中的缓存技术是一种优化手段,用于提高应用程序的性能和响应速度。缓存技术通过将计算结果或者经常访问的数据存储在快速访问的存储介质中,以便下次需要时可以更快地获取。...【详细内容】
2024-01-30  编程技术汇    Tags:Java   点击:(72)  评论:(0)  加入收藏
JDK17 与 JDK11 特性差异浅谈
从 JDK11 到 JDK17 ,Java 的发展经历了一系列重要的里程碑。其中最重要的是 JDK17 的发布,这是一个长期支持(LTS)版本,它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性...【详细内容】
2024-01-26  政采云技术  51CTO  Tags:JDK17   点击:(88)  评论:(0)  加入收藏
Java并发编程高阶技术
随着计算机硬件的发展,多核处理器的普及和内存容量的增加,利用多线程实现异步并发成为提升程序性能的重要途径。在Java中,多线程的使用能够更好地发挥硬件资源,提高程序的响应...【详细内容】
2024-01-19  大雷家吃饭    Tags:Java   点击:(105)  评论:(0)  加入收藏
这篇文章彻底让你了解Java与RPA
前段时间更新系统的时候,发现多了一个名为Power Automate的应用,打开了解后发现是一个自动化应用,根据其描述,可以自动执行所有日常任务,说的还是比较夸张,简单用了下,对于office、...【详细内容】
2024-01-17  Java技术指北  微信公众号  Tags:Java   点击:(95)  评论:(0)  加入收藏
Java 在 2023 年仍然流行的 25 个原因
译者 | 刘汪洋审校 | 重楼学习 Java 的过程中,我意识到在 90 年代末 OOP 正值鼎盛时期,Java 作为能够真正实现这些概念的语言显得尤为突出(尽管我此前学过 C++,但相比 Java 影响...【详细内容】
2024-01-10  刘汪洋  51CTO  Tags:Java   点击:(74)  评论:(0)  加入收藏
站内最新
站内热门
站内头条