AICon上海「Agent与多模态解决方案专场」火热来袭!即刻报名,与创新同行~ 了解详情
写点什么

JEP 505 发布:Java 结构化并发迎来第五次预览版,关键 API 优化

  • 2025-05-19
    北京
  • 本文字数:2444 字

    阅读完需:约 8 分钟

大小:863.09K时长:04:54
JEP 505 发布:Java 结构化并发迎来第五次预览版,关键 API 优化

JEP 505(结构化并发第五次预览版)已被确定为 JDK 25 版本的正式特性(Targeted 状态)。该 API 历经五次预览迭代,旨在简化并行任务管理并为开发者提供更清晰、更安全的框架,特别是在使用虚拟线程相关场景。最新预览版优化了此前孵化版本(JEP 428、JEP 437)和预览版本(JEP 453、JEP 462、JEP 480、JEP 499)中引入的 API。最显著的变化是,StructuredTaskScope 不再通过公共构造函数实例化,开发者现在需通过静态工厂方法(如 StructuredTaskScope.open())开启作用域。这一改动明确了默认行为,并为更丰富的任务完成策略铺平了道路。


根据 JEP 规范,结构化并发主要解决并行编程中的三个核心问题:

1.严格限定子任务的生命周期仅在明确定义的父作用域内

2.实现可靠的取消机制,避免资源泄漏

3.通过结构化线程层级增强可观测性


该 API 的核心是java.util.concurrent.StructuredTaskScope类,它负责管理一组并发子任务。开发者在作用域内 fork 子任务,随后统一等待其完成(join),作用域将自动维护这些任务的执行边界。


以下面这段代码为例:


try (var scope = StructuredTaskScope.open()) {    Subtask<String> user = scope.fork(() -> fetchUser(userId));    Subtask<List<Order>> orders = scope.fork(() -> fetchOrders(userId));        scope.join();  // Wait for all subtasks        // Process results or handle exceptions    String userName = user.get();    List<Order> userOrders = orders.get();}
复制代码

 

这段代码展示了结构化并发的基本使用模式。它通过新的工厂方法创建作用域,并行派发两个任务(获取用户数据和订单数据),使用join()等待两者完成,最后获取结果。该作用域能确保在退出代码块时,所有子任务要么已完成,要么被自动取消。


作为对比,这里展示了相同功能在之前结构化并发 API 预览版中的实现方式:


try (var scope = new StructuredTaskScope<>()) {      Subtask<String> user = scope.fork(() -> fetchUser(userId));    Subtask<List<Order>> orders = scope.fork(() -> fetchOrders(userId));        scope.join();        // Process results or handle exceptions    String userName = user.get();    List<Order> userOrders = orders.get();}
复制代码

 

虽然基本结构类似,但第五次预览版引入了诸如StructuredTaskScope.open()的工厂方法,取代了基于构造函数的实例化方式。这一改进提升了 API 的可读性,同时为库的维护者提供了更大灵活性,使得未来的演进不会破坏其兼容性。


无参的open()工厂方法会创建一个“快速失败”的作用域:如果任何子任务抛出异常,其余任务会被中断,且join()会重新抛出该异常。开发者也可以通过open(Joiner)提供自定义策略,例如:


// Return the first successful result, cancel the rest<T> T race(Collection<Callable<T>> tasks) throws InterruptedException {    try (var scope = StructuredTaskScope.open(            Joiner.<T>anySuccessfulResultOrThrow())) {        tasks.forEach(scope::fork);        return scope.join();    }}
复制代码

 

每次 fork 都会会启动一个子任务(默认在虚拟线程上执行),并返回一个Subtask句柄,其get()方法仅在join()完成后调用才是安全的。该作用域强制结构化约束:若从非属主线程调用fork()join(),或在未关闭作用域的情况下退出代码块,都将抛出StructureViolationException


工厂方法allSuccessfulOrThrow()返回一个新的合并器(joiner):当所有子任务都成功完成时,它会生成一个包含所有子任务的流。


<T> List<T> runConcurrently(Collection<Callable<T>> tasks) throws InterruptedException {    try (var scope = StructuredTaskScope.open(Joiner.<T>allSuccessfulOrThrow())) {        tasks.forEach(scope::fork);        return scope.join().map(Subtask::get).toList();    }}
复制代码


若一个或多个子任务失败,则 join() 会抛出 FailedException,并以其中一个失败子任务的异常作为其根因。

 

Joiner 接口提供了三个额外的工厂方法:awaitAll() 会返回一个等待所有子任务完成(无论成功与否)的新合并器,;awaitAllSuccessfulOrThrow() 会返回一个要求所有子任务必须全部成功完成的新合并器;allUntil(Predicate<Subtask<? extends T>> isDone) 会返回一个新的合并器,在所有子任务成功完成,或某个已完成子任务满足谓词条件时,取消当前作用域并返回所有子任务的流。

 

在使用任何 Joiner 时,必须为每个 StructuredTaskScope 创建新的 Joiner 实例。Joiner 对象不可跨作用域复用,也不应在作用域关闭后重复使用。

 

开发者可直接实现 Joiner 接口以自定义完成策略。该接口包含两个泛型参数:表示作用域内子任务返回类型的 T;表示 join() 方法返回类型的 R


其接口定义如下:

public interface Joiner<T, R> {    public default boolean onFork(Subtask<? extends T> subtask);    public default boolean onComplete(Subtask<? extends T> subtask);    public R result() throws Throwable;}
复制代码

 

当调用 fork() 创建子任务时,会触发 onFork() 方法;而当子任务完成时,则会调用 onComplete() 方法。


规范还明确了一点,作用域内的子任务会继承 ScopedValue 绑定值。如果作用域的所有者线程从已绑定的 ScopedValue 读取了某个值,那么每个子任务读取到的也将是同一个值。

 

此外,该 JEP 还扩展了为虚拟线程引入的 JSON 线程转储格式,新增了对 StructuredTaskScope 的支持,能够以层级结构展示线程分组关系:

 

$jcmd <pid> Thread.dump_to_file -format=json <file>
复制代码

 

每个作用域对应的 JSON 对象包含该作用域内派发的所有线程数组和各线程对应的调用栈信息(stack traces)。

 

作为预览特性,OpenJDK 团队鼓励开发者在 JDK 25 中试用这第五个迭代版本并积极反馈,这些反馈对 API 的最终定型至关重要。


查看英文原文:JEP 505 Delivers Fifth Preview of Java's Structured Concurrency with Key API Refinements

2025-05-19 16:00265

评论

发布
暂无评论

工作10年,面试超过500人想进阿里的同学,总结出的108道面试题

Java MySQL redis spring JVM

如何避免企业在碳排放数据上造假?

石云升

学习笔记 碳中和 碳交易

十面阿里Java岗,看我怎么吊打面试官!

Java 程序员 后端

华为java工程师的提升程序员实力的几点建议

Java 程序员 后端

博客之星:我去,你竟然还不会用 synchronized

Java 程序员 后端

原来书中说的JVM默认垃圾回收器是错的!

Java 程序员 后端

双非本科七面成功入职阿里面经分享!(附面试原题+复盘笔记)

Java 程序员 后端

双非本科进不了大厂?阿里技术四面+交叉面+HR面,成功拿到offer

Java spring 程序员 mybatis

初次远程面试蚂蚁金服,三面过后本以为凉凉,没想到直接被录取了

Java 程序员 后端

动手造轮子:实现一个简单的-AOP-框架

Java 程序员 后端

南邮《网络技术与应用》4次作业

Java 程序员 后端

剖根问底:Java 不能实现真正泛型的原因是什么?

Java 程序员 后端

怎样选择最合适的Linux发行版?23个版本横向对比,总有适合你的?

奔着腾讯去

Linux

厉害!腾讯T3-2都还在学的微服务+MySQL+Kafka+boot2

Java 程序员 后端

华为安全技术专家与Linux内核到底发生了什么?这本小册子是怎么回事?

Java 程序员 后端

初探DispatcherServlet#doDispatch

Java 程序员 后端

制作Docker镜像,用来编译OpenJDK11源码

Java 程序员 后端

区块链编程七大语言,使用最多的竟是Java

Java 程序员 后端

前华为18A架构师,总结“RabbitMQ”开发手册,已开源

Java 程序员 后端

前后端项目练习(整合Spring)

Java 程序员 后端

互联网通信云盛会WICC广州站绿色报名通道开启

融云 RongCloud

又一巅峰神作!14年工作经验大佬出品“JVM&G1 GC深入学习手册”

Java 程序员 后端

初识Java语言(六)- 多态、抽象类以及接口

Java 程序员 后端

力扣前400题解答笔记,全被字节大神整理到了这份文档里

Java 程序员 后端

十个超酷的java谋生方式,你喜欢吗?

Java 程序员 后端

北上广深,2020,多少K的Java程序员应该懂高并发多线程和JVM优化

Java 程序员 后端

华为初面+综合面试(Java技术面)附上面试题

Java 程序员 后端

华山论剑!滴滴CTO五轮面试真是太刺激了,已拿到offer

Java 程序员 后端

初识Servlet

Java 程序员 后端

别再找了,这就是全网最全的SpringBean的作用域管理!

Java 程序员 后端

别再说你不会-JVM-性能监控和调优了,看完这篇再发言!

Java 程序员 后端

JEP 505 发布:Java 结构化并发迎来第五次预览版,关键 API 优化_编程语言_A N M Bazlur Rahman_InfoQ精选文章