当前位置:首页 > 科技  > 软件

提升系统吞吐量,详解JDK21虚拟线程,炸裂

来源: 责编: 时间:2024-06-06 17:36:22 214观看
导读环境:JDK211. 虚拟线程简介虚拟线程是轻量级的线程,可以减少编写、维护和调试高吞吐量并发应用程序的工作量。线程是可以调度的最小处理单元。它与其他类似单元并发运行,而且在很大程度上是独立运行的。它是java.lang.Th

环境:JDK219vz28资讯网——每日最新资讯28at.com

1. 虚拟线程简介

虚拟线程是轻量级的线程,可以减少编写、维护和调试高吞吐量并发应用程序的工作量。线程是可以调度的最小处理单元。它与其他类似单元并发运行,而且在很大程度上是独立运行的。它是java.lang.Thread的一个实例。线程有两种,平台线程和虚拟线程。9vz28资讯网——每日最新资讯28at.com

2. 什么是平台线程

平台线程被实现为操作系统(OS)线程的薄包装器。平台线程在其底层操作系统线程上运行Java代码,平台线程在平台线程的整个生命周期中捕获其操作系统线程。因此,可用平台线程的数量受限于操作系统线程的数量。9vz28资讯网——每日最新资讯28at.com

平台线程通常有一个比较大的线程堆栈和由操作系统维护的其他资源。它们适用于运行所有类型的任务,但可能是有限的资源。9vz28资讯网——每日最新资讯28at.com

3. 什么是虚拟线程

与平台线程一样,虚拟线程也是java.lang.thread的一个实例。然而,虚拟线程并没有绑定到特定的操作系统线程。虚拟线程仍然在操作系统线程上运行代码。但是,当虚拟线程中运行的代码调用阻塞I/O操作时,Java运行时会挂起虚拟线程,直到可以恢复为止。与挂起的虚拟线程相关联的OS线程现在可以自由地执行其他虚拟线程的操作。9vz28资讯网——每日最新资讯28at.com

虚拟线程的实现方式与虚拟内存类似。为了模拟大量内存,操作系统将大量虚拟地址空间映射到有限的RAM。同样,为了模拟大量线程,Java运行时将大量虚拟线程映射到少量操作系统线程。9vz28资讯网——每日最新资讯28at.com

与平台线程不同,虚拟线程通常有一个浅调用堆栈,只执行一个HTTP客户端调用或一个JDBC查询。尽管虚拟线程支持线程本地变量和可继承的线程本地变量,但应该仔细考虑使用它们,因为单个JVM可能支持数百万个虚拟线程。9vz28资讯网——每日最新资讯28at.com

虚拟线程适用于运行大部分时间被阻塞的任务,这些任务通常等待I/O操作完成。然而,它们并不适用于长时间运行的CPU密集型操作。9vz28资讯网——每日最新资讯28at.com

4. 为什么使用虚拟线程

在高吞吐量并发应用程序中使用虚拟线程,尤其是那些由大量并发任务组成、花费大量时间等待的应用程序。服务器应用程序是高吞吐量应用程序的示例,因为它们通常处理许多执行阻塞I/O操作(如获取资源)的客户端请求。9vz28资讯网——每日最新资讯28at.com

虚拟线程不是更快的线程;它们运行代码的速度并不比平台线程快。它们的存在是为了提供规模(更高的吞吐量),而不是速度(更低的延迟)。9vz28资讯网——每日最新资讯28at.com

5. 创建虚拟线程

Thread和Thread.Builder APIs提供了创建平台线程和虚拟线程的方法。java.util.concurrent.Executors类还定义了创建ExecutorService的方法,该方法为每个任务启动一个新的虚拟线程。9vz28资讯网——每日最新资讯28at.com

5.1 Thread & Thread.Builder创建虚拟线程

调用Thread.ofVirtual()方法创建一个Thread.Builder实例,用于创建虚拟线程。如下示例:9vz28资讯网——每日最新资讯28at.com

Thread t= Thread.ofVirtual().start(() -> System.out.println("Hello")) ;t.join() ;

Thread.Builder接口允许创建具有公共线程属性(如线程名称)的线程。Thread.Builder.OfPlatform子接口创建平台线程,而Thread.Builder.OfVirtual创建虚拟线程。9vz28资讯网——每日最新资讯28at.com

下面的示例使用Thread.Builder接口创建一个名为T-VM的虚拟线程,如下示例:9vz28资讯网——每日最新资讯28at.com

Thread.Builder builder = Thread.ofVirtual().name("T-VM") ;Runnable task = () -> {  System.out.println("执行任务") ;} ;Thread t = builder.start(task) ;System.err.printf("线程名称: %s%n", t.getName()) ;t.join() ;

输出结果:9vz28资讯网——每日最新资讯28at.com

执行任务线程名称T-VM

下面的示例创建并启动两个具有Thread.Builder的虚拟线程:9vz28资讯网——每日最新资讯28at.com

Thread.Builder builder = Thread.ofVirtual().name("vm - worker - ", 0);Runnable task = () -> {  System.out.printf("线程ID: %d%n", Thread.currentThread().threadId());} ;// 线程 "vm - worker - 0"Thread t1 = builder.start(task) ;   t1.join();System.out.println(t1.getName() + " terminated") ;// 线程 "vm - worker - 1"Thread t2 = builder.start(task) ;   t2.join() ;  System.out.println(t2.getName() + " terminated") ;

输出结果:9vz28资讯网——每日最新资讯28at.com

线程ID: 21vm - worker - 0 terminated线程ID: 24vm - worker - 1 terminated

以上是通过Thread.Builder创建虚拟线程的简单示例。9vz28资讯网——每日最新资讯28at.com

5.2 Executors创建虚拟线程

Executors允许将线程管理和创建与应用程序的其余部分分离。9vz28资讯网——每日最新资讯28at.com

ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor() ;// submit Runnable任务Future<?> future = executor.submit(() -> System.out.println("Running thread")) ;future.get() ;System.out.println("Task completed") ;

上面示例每当调用ExecutorService.submit(Runnable)时,都会创建一个新的虚拟线程并开始运行该任务。9vz28资讯网——每日最新资讯28at.com

6. 虚拟线程调度

操作系统在平台线程运行时进行调度。然而,Java运行时会在虚拟线程运行时进行调度。当Java运行时调度虚拟线程时,它将虚拟线程分配或挂载到平台线程上,然后操作系统像往常一样调度该平台线程。这个平台线程称为载体(carrier)。运行一些代码后,虚拟线程可以从它的载体卸载。这通常发生在虚拟线程执行阻塞I/O操作时。当一个虚拟线程从其宿主中卸载后,宿主就处于空闲状态,这意味着Java运行时调度器可以在其上分配另一个虚拟线程。9vz28资讯网——每日最新资讯28at.com

虚拟线程被绑定到其宿主(平台线程)时,在阻塞操作期间无法将其卸载。虚拟线程在以下情况下会被绑定:9vz28资讯网——每日最新资讯28at.com

  • 虚拟线程在同步块或方法内运行代码
  • 虚拟线程运行本机方法或外部函数

7. 虚拟线程应用指南

虚拟线程是由Java运行时而不是操作系统实现的Java线程。虚拟线程和传统线程(平台线程)的主要区别在于,可以很容易地在同一个Java进程中运行大量甚至数百万个活动的虚拟线程。正是它们的数量赋予了虚拟线程强大的能力,通过允许服务器并发处理更多请求,它们可以更高效地运行以"thread-per-request"风格编写的服务器应用程序,从而提高吞吐量,减少硬件浪费。9vz28资讯网——每日最新资讯28at.com

虚拟线程可以显著提高以thread-per-request风格编写的服务器的吞吐量,而不是延迟。在这种风格中,服务器在整个持续时间内使用一个线程来处理每个传入的请求。它至少占用一个线程,因为在处理单个请求时,你可能希望使用更多线程并发地执行某些任务。9vz28资讯网——每日最新资讯28at.com

阻塞平台线程的代价是昂贵的,因为它会占用线程——这是一种相对稀缺的资源——而线程并没有做很多有意义的工作。因为虚拟线程可能很多,所以阻塞它们是廉价的,也是值得鼓励的。因此,应该使用简单的同步风格并使用阻塞I/O API编写代码。9vz28资讯网——每日最新资讯28at.com

如下代码以非阻塞异步风格编写,不会从虚拟线程中获得太多好处。9vz28资讯网——每日最新资讯28at.com

HttpClient client = ... ;Executor pool = Executors.newVirtualThreadPerTaskExecutor() ;CompletableFuture.supplyAsync(() -> {  HttpRequest request = HttpRequest.newBuilder(URI.create("http://localhost:8088/users/info")).build() ;  BodyHandler<String> bodyHandler = ... ;  try {    return client.send(request , bodyHandler) ;  }}, pool).thenCompose(url -> getBodyAsync(url, HttpResponse.BodyHandlers.ofString())).thenApply(info::findImage).thenAccept(this::process).exceptionally(t -> { t.printStackTrace(); return null; });

相反,以下代码以同步风格编写,并使用简单的阻塞IO,将受益匪浅:9vz28资讯网——每日最新资讯28at.com

try {   String page = getBody(info.getUrl(), HttpResponse.BodyHandlers.ofString());   String imageUrl = info.findImage(page);   byte[] data = getBody(imageUrl, HttpResponse.BodyHandlers.ofByteArray());      info.setImageData(data);   process(info);}

8. 不要池化虚拟线程

关于虚拟线程最难理解的是,虽然它们与平台线程具有相同的行为,但它们不应该表示相同的程序概念。9vz28资讯网——每日最新资讯28at.com

平台线程很少,因此是一种宝贵的资源。宝贵的资源需要管理,管理平台线程的最常见方法是使用线程池。接下来你需要回答的问题是,线程池中应该有多少个线程?9vz28资讯网——每日最新资讯28at.com

但是虚拟线程是很多的,因此每个线程不应该代表某种共享的、池化的资源,而应该代表一个任务。9vz28资讯网——每日最新资讯28at.com

将n个平台线程转换为n个虚拟线程几乎没有好处;相反,需要转换的是任务。9vz28资讯网——每日最新资讯28at.com

为了将每个应用任务表示为一个线程,不要像下面的例子那样使用共享线程池:9vz28资讯网——每日最新资讯28at.com

Future<ResultA> f1 = sharedThreadPoolExecutor.submit(task1);Future<ResultB> f2 = sharedThreadPoolExecutor.submit(task2);// ... use futures

相反,使用虚拟线程9vz28资讯网——每日最新资讯28at.com

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {   Future<ResultA> f1 = executor.submit(task1);   Future<ResultB> f2 = executor.submit(task2);   // ... use futures}

上面的代码仍然使用ExecutorService,但从Executors.newVirtualThreadPerTaskExecutor()返回的代码没有使用线程池。相反,它为每个提交的任务创建一个新的虚拟线程。9vz28资讯网——每日最新资讯28at.com

此外,ExecutorService本身是轻量级的,我们可以像创建任何简单对象一样创建一个新的对象。这使得我们可以依赖新添加的ExecutorService#close方法和try-with-resources构造。close方法会在try块结束时隐式调用,它会自动等待所有提交给ExecutorService的任务(即由ExecutorService生成的所有虚拟线程)结束。9vz28资讯网——每日最新资讯28at.com

对于扇出场景来说,这是一个特别有用的模式,在这种场景中,同时执行多个对不同服务调用,如下面的示例所示:9vz28资讯网——每日最新资讯28at.com

void handle() throws Exception {  URL url1 = URI.create("http://www.pack.com").toURL() ;  URL url2 = URI.create("http://www.akf.com").toURL() ;  try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {    var future1 = executor.submit(() -> fetchURL(url1));    var future2 = executor.submit(() -> fetchURL(url2));    System.out.printf("result1: %s, result2: %s%n", future1.get(),future2.get()) ;  }}String fetchURL(URL url) throws IOException {  try (var in = url.openStream()) {    return new String(in.readAllBytes(), StandardCharsets.UTF_8);  }}

你应该创建一个新的虚拟线程,如上所示,即使是小的、短期的并发任务。9vz28资讯网——每日最新资讯28at.com

9. 避免长时间频繁的Pinning

当前实现虚拟线程的一个限制是,在同步的块或方法内部执行阻塞操作会导致JDK的虚拟线程调度器阻塞一个操作系统线程,而在同步的块或方法外部执行阻塞操作则不会。这种情况称为“Pinning”。如果阻塞操作持续时间长且频繁,Pinning可能会对服务器的吞吐量产生负面影响。保护短期的操作,例如内存操作,或者使用同步块或方法的不频繁操作,应该不会有任何负面影响。9vz28资讯网——每日最新资讯28at.com

对于长时间又频繁的地方应该使用ReentrantLock替换synchronized 。9vz28资讯网——每日最新资讯28at.com

synchronized(lockObj) {  frequentIO() ;}// 替换为lock.lock();try {  frequentIO() ;} finally {  lock.unlock() ;}


9vz28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-92454-0.html提升系统吞吐量,详解JDK21虚拟线程,炸裂

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 强势问鼎!强力巨彩斩获 2023 年 LED 显示屏销量第一!

下一篇: 代码中预编译常见指令用法

标签:
  • 热门焦点
  • K6:面向开发人员的现代负载测试工具

    K6 是一个开源负载测试工具,可以轻松编写、运行和分析性能测试。它建立在 Go 和 JavaScript 之上,它被设计为功能强大、可扩展且易于使用。k6 可用于测试各种应用程序,包括 Web
  • 分享六款相见恨晚的PPT模版网站, 祝你做出精美的PPT!

    1、OfficePLUSOfficePLUS网站旨在为全球Office用户提供丰富的高品质原创PPT模板、实用文档、数据图表及个性化定制服务。优点:OfficePLUS是微软官方网站,囊括PPT模板、Word模
  • Flowable工作流引擎的科普与实践

    一.引言当我们在日常工作和业务中需要进行各种审批流程时,可能会面临一系列技术和业务上的挑战。手动处理这些审批流程可能会导致开发成本的增加以及业务复杂度的上升。在这
  • 量化指标是与非:挽救被量化指标扼杀的技术团队

    作者 | 刘新翠整理 | 徐杰承本文整理自快狗打车技术总监刘新翠在WOT2023大会上的主题分享,更多精彩内容及现场PPT,请关注51CTO技术栈公众号,发消息【WOT2023PPT】即可直接领取
  • 一篇文章带你了解 CSS 属性选择器

    属性选择器对带有指定属性的 HTML 元素设置样式。可以为拥有指定属性的 HTML 元素设置样式,而不仅限于 class 和 id 属性。一、了解属性选择器CSS属性选择器提供了一种简单而
  • 当家的盒马,加速谋生

    来源 | 价值星球Planet作者 | 归去来自己&ldquo;当家&rdquo;的盒马,开始加速谋生了。据盒马官微消息,盒马计划今年开放生鲜供应链,将其生鲜商品送往食堂。目前,盒马在上海已经与
  • 年轻人的“职场羞耻感”,无处不在

    作者:冯晓亭 陶 淘 李 欣 张 琳 马舒叶来源:燃次元&ldquo;人在职场,应该选择什么样的着装?&rdquo;近日,在网络上,一个与着装相关的帖子引发关注,在该帖子里,一位在高级写字楼亚洲金
  • 引领旗舰级影像能力向中端机普及 OPPO K11 系列发布 1799 元起

    7月25日,OPPO正式发布K系列新品—— OPPO K11 。此次 K11 在中端手机市场长期被忽视的影像板块发力,突破性地搭载索尼 IMX890 旗舰大底主摄,支持 OIS
  • 最薄的14英寸游戏笔记本电脑 Alienware X14已可以购买

    2022年1月份在国际消费电子展(CES2022)上首次亮相的Alienware新品——Alienware X14现在已经可以购买了,这款笔记本电脑被誉为世界上最薄的 14 英寸游戏笔
Top