java 19新特性简要介绍

Java 19引入虚拟线程(JEP 425)和结构化并发(JEP 428)作为重要特性,旨在提升并发性能和简化多线程编程。虚拟线程提供轻量级实现,允许多个线程共享操作系统线程,大幅提高应用程序吞吐量。结构化并发则通过任务作用域管理,简化错误处理和取消工作,提高多线程代码的可靠性和可观测性。

0 前言

Java19于2022年9月底发布。全新的Java版本提供7个JDK增强建议,帮助开发人员提供工作效率,优化Java语言并提升平台的性能、稳定性和安全性。Java19的主要功能将于本月17日至20日在拉斯维加斯举办的JavaOne大会上隆重揭晓。

Java19将提供来自OpenJDK Project Amber的语言改进(记录模式和switch匹配模式);支持与非Java代码互操作的本机库增加功能(外部函数和内存API),并利用来自OpenJDK Project Panama的向量指令(向量API);以及Project Loom(虚拟线程和结构化并发)。这将大大减少在Java中编写和维护高吞吐量并发应用的工作量。IDC软件开发研究副总裁Arnal Dayaratna表示:“Java开发人员越来越需要借助工具来协助他们高效地构建功能强大的应用,以在云端、本地和混合环境中进行部署,Java19的增强功能充分考虑了这些方面,充分说明Java生态系统能够很好地满足开发人员和企业当前以及未来需求”。

官方链接:

甲骨文正式发布 Java 19

1 Java19提供的重要功能包括:

  1. JEP405:记录模式。支持用户嵌套记录模式和类型模式,以创建强大、声明性且可组合的数据导航和处理形式,从而扩展模式匹配,实现更复杂的数据查询。

官方链接:JEP 405: Record Patterns (Preview)

  1. JEP 427:switch模式匹配。根据某些模式来测试表达式,以进行switch表达式和语句模式匹配,让用户可以安全、简洁的表达面向数据的复杂查询。

官方链接:JEP 427: Pattern Matching for switch (Third Preview)

  1. JEP 424:外部函数和内存API。Java 程序可以更容易地与 Java 运行时之外的代码和数据进行互操作。通过有效调用外部函数(即 JVM 之外的代码),以及安全地访问外部内存(即不受 Java Virtual Machine [JVM] 管理的内存),API 可以通过纯 Java 开发模型调用本地库和处理原生数据,从而提高易用性、性能、灵活性和安全性。

官方链接:JEP 424: Foreign Function & Memory API (Preview)

  1. JEP 426:矢量 API。允许以一种在运行时,可靠地编译为支持的 CPU 架构上的向量指令方式表达向量计算,从而实现优于等效标量计算的性能。

官方链接:JEP 426: Vector API (Fourth Incubator)

  1. JEP 422:Linux/RISC-V 端口。通过将此端口集成到 JDK 主线存储库中中,以便于进行 Linux/RISC-V 实施。

官方链接:JEP 422: Linux/RISC-V Port

  1. JEP 425:虚拟线程。通过向 Java 平台引入轻量级虚拟线程,显著减少编写、维护和观察高吞吐量并发应用的工作量。开发人员可以通过虚拟线程轻松使用现有的 JDK 工具和技术对并发应用进行故障排除、调试和分析。

官方链接:JEP 425: Virtual Threads (Preview)

  1. JEP 428:结构化并发。通过简化多线程编程和将运行于不同线程中的多个任务视为一个工作单元,简化错误处理和取消工作并提高可靠性和可观测性。

官方链接:JEP 428: Structured Concurrency (Incubator)

官方文档中对每个新增的特性都有非常详细的描述说明。感兴趣的可以去官方查看。

2 虚拟线程

JEP 425虚拟线程可能是JDK19中最值得期待预览的特性之一了。下面对虚拟线程进行较为详细的介绍。

Java虚拟线程引入Java平台。虚拟线程是轻量级线程,可以有效减少编写和维护的工作量。启用Java.lang.ThreadAPI的现有代码以最小的更改采用虚拟线程。可以使用现有的JDK工具轻松地对虚拟线程进行故障排除、调试和分析。

虚拟线程简要描述

JDK中的每一个实例都是一个平台线程。平台线程在底层操作系统线程上运行Java代码,并在代码的整个生命周期捕获操作系统线程。平台线程数受限于OS线程数。虚拟线程是在底层OS线程上运行代码,但在代码的整个生命周期内不捕获OS线程实例。这意味着多个虚拟线程可以在同一个操作系统上运行他们的Java代码,从而有效共享它。平台线程独占宝贵的操作系统线程。但虚拟线程没有,虚拟线程的数量可以远远大于操作系统线程的数量。

虚拟线程是由JDK而不是由OS提供的线程的轻量级实现。它们是用户模式线程的一种形式,在其他多线程语言中已有实现。如Go中的goroutine。用户模式线程甚至在 Java 的早期版本中被称为“绿色线程”。 Java 的绿色线程都共享一个 OS 线程(M:1 调度)并最终被平台线程超越,实现为 OS 线程的包装器(1:1 调度)。虚拟线程采用 M:N 调度,其中大量 (M) 虚拟线程被调度为在较少数量 (N) 的 OS 线程上运行。

虚拟线程的简单使用:

开发者可以选择是使用虚拟线程还是平台线程。这是一个创建大量虚拟线程的示例程序。程序首先获得一个ExecutorService,它将为每个提交的任务创建一个新的虚拟线程。然后它提交 10,000 个任务并等待所有任务完成:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {

    IntStream.range(0, 10_000).forEach(i -> {

        executor.submit(() -> {

            Thread.sleep(Duration.ofSeconds(1));

            return i;

        });

    });

}  // executor.close() is called implicitly, and waits

该例子创建10000个虚拟线程,同时运行这些代码。JDK在操作系统的少数线程上运行,最少可能只有一个。

如果该程序使用ExecutorService为每个任务创建平台线程Executors.newCachedThreadPool()。这ExecutorService将尝试创建10000个平台线程,从而创建10000个OS线程,程序可能就崩溃了。相反,如果程序使用ExecutorService从池中获取平台线程的Executors.newFixedThreadPool(200)。这ExecutorService将创建200个平台线程供所有 10,000 个任务共享,因此许多任务将按顺序运行而不是同时运行,并且程序需要很长时间才能完成。对于这个程序,具有 200个平台线程的池只能实现每秒200个任务的吞吐量,而虚拟线程可以实现大约每秒 10,000 个任务的吞吐量(在充分预热之后)。此外,如果将示例程序中的虚拟线程数更改为1000000,那么程序将提交 1,000,000 个任务,创建 1,000,000 个并发运行的虚拟线程,并且(在充分预热后)达到大约 1,000,000 个任务/秒的吞吐量。

虚拟线程可以显著的提高应用程序吞吐量,它的存在是为了提高规模,而不是更低的延迟。

3 结构化并发

通过引入结构化并发的API来简化多线程编程。具体来说,结构化并发让开发人员以类似单线程的方式来编写多线程代码,尽可能地对开发人员屏蔽多线程的相关细节。结构化并发将在不同线程中运行的多个任务视为一个工作单元,从而简化错误处理逻辑,提高可靠性并增强可观察性。

     在使用结构化并发之前,首先需要创建一个结构化任务作用域,然后在这个作用域中创建并执行任务。这些任务的生命周期由作用域负责管理。当作用域被关闭时,其中所包含的任务都会结束。这意味着开发人员不用处理任务的失败、取消和超时等情况,完全由底层平台支持。

     在一个作用域中,其中创建的任务也可以创建自己的作用域,用来管理该任务的子任务。这就形成了一个任务组成的树形结构。

     使用结构化并发的基础类是jdk.incubator.concurrent.StructuredTaskScope。StructuredTaskScope 表示的是一个使用结构化并发的作用域。

主要方法如下表所示:

方法

说明

fork(Callable<? extends U> task)

启动一个线程来执行任务

join()

等待所有任务执行完成或者当前作用域被关闭

joinUntil(Instant deadline)

与join()相同,只不过设置了终止时间

shutdown()

结束任务作用域

close()

关闭任务作用域

使用 StructuredTaskScope 的基本流程如下:

1)主任务创建一个 StructuredTaskScope 对象。

2)使用 fork 方法来创建子任务。

3)使用 join 或 joinUntil 方法来等待子任务完成或取消。

4)在 join 或 joinUntil 方法返回之后,处理子任务中可能出现的错误,并使用子任务产生的结果。

5)关闭作用域对象,一般使用 try-with-resources 可以自动进行关闭。

上面的基本流程看起来也比较繁琐,但重点在于思维方式的变化。结构化并发的思维方式,更加类似传统的单线程应用,因此更容易理解。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值