【Java】基于jdk21的StructuredTaskScope编程的简单探索

JDK 19 添加了Structured Concurrency相关的功能,与多线程相关,StructuredTaskScope正是其中的一个类。在jdk19/jdk20中,StructuredTaskScope位于jdk.incubator.concurrent包下,incubator是孵化器的意思。在jdk21中位于java.util.concurrent下,从孵化器正式纳入java.util中了。

StructuredTaskScope从jdk19到jdk21,还是有些许变化的,比如fork方法的返回值等等,本文使用的是jdk21编程

【参考】

1. 使用jdk21的准备

jdk下载:www.oracle.com/cn/java/tec...

需要下载2023.3版本的IntelliJ IDEA

2. 开启--enable-preview

安装好后,想要使用StructuredTaskScope会报错:

less 复制代码
java: java.util.concurrent.StructuredTaskScope 是预览 API,默认情况下处于禁用状态。
  (请使用 --enable-preview 以启用预览 API)

解决办法,可以在pom.xml中加入:

xml 复制代码
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>21</source>
                <target>21</target>
                <compilerArgs>--enable-preview</compilerArgs>
            </configuration>
        </plugin>
    </plugins>
</build>

3. Structured Concurrency

传统的jdk中的多线程提供的方式都是Unstructured,比如Thread创建,需要手动的管理,虽然有一些框架,如适合异步的CompletableFuture或者ExcutorService(以线程池的方式管理线程)。但总体上,这些类提供的API都是非常灵活的,即怎么写都可能能达到想要的效果,但灵活的编程也意味着不可控。

所以在jdk19提供了Structured Concurency的概念,它主要提供了一种更为容易的方式来处理任务以及子任务。

4. 使用StructuredTaskScope

StructuredTaskScope是一个scope的概念,在这个scope中,我们可以创建自己的subtask,在最后join(),拿到我们想要的结果。

我们可以创建StructuredTaskScope本身的instance,或它的子类:

  • StructuredTaskScope<T>(),也可以创建自己的类再继承它。
  • StructuredTaskScope.ShutdownOnFailure(),子类之一。如果遇到失败的subtask,那么会关闭整个scope。如果我们需要得到所有子任务的结果(相当于invokeAll()),那么如果子任务有失败,那么其它的子任务会中止行动。
  • StructuredTaskScope.ShutdownOnSuccess(),子类之一。捕捉第一个成功的子任务,如果有,就会关闭整个scope,这时候可能会中止其它的子任务,唤起scope的owner,相当于invokeAny()。

在上述参考链接中,第二个链接《Walkthrough of the Java StructuredTaskScope Code》视频中有个比喻:

  • ShutdownOnFailure的过程相当于子任务是一群整齐划一的士兵,所以需要一起行动,如果遇到撤退事件,也是所有的士兵一起撤退:

  • ShutdownOnSuccess的过程相当于子任务之间的有奖竞猜,最先按下抢答按钮,谁就有回答的权利,这时候并不在乎其它选手的答案了:

4.1 ShutdownOnFailure示例

  • 首先,ShutdownOnFailure用完后需要关闭,它实现了AutoCloseable接口,可以用try-with-resources语句来写。
  • scope.join():这个方法会等所有的subtask结束后进行一个"汇总"。
  • 如果在join()方法后加上throwIfFailed(),那么在遇到单个subtask exception时,会抛出异常ExecutionException
csharp 复制代码
    public String getResources() throws InterruptedException, ExecutionException {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            Supplier<String> orderSupplier = scope.fork(() -> getOrder());
            Supplier<String> userSupplier = scope.fork(() -> getUser());

            scope.join().throwIfFailed();
            String resources = orderSupplier.get() + ", " + userSupplier.get();
            return resources;
    }

    // 用来模拟取order list的过程
    public static String getOrder() throws InterruptedException {
        Thread.sleep(2000L); // mock IO cost
        return "order list";
    }

    // 用来模拟取user list的过程
    public static String getUser() throws InterruptedException {
        Thread.sleep(5000L); // mock IO cost
        return "user list";
    }
  • getResource()方法返回:order list, user list
  • throwIfFailed()还可以接收参数Function<Throwable, ? extends X> esf,即可以新定义一个Function来处理异常。 比如:
typescript 复制代码
public Function<Throwable, IllegalArgumentException> handleError(String message) {
    return throwable -> {
        log.error("do something here...");
        return new IllegalArgumentException(message);
    };
}

使用:

csharp 复制代码
scope.join().throwIfFailed(handleError("invalid error"));

4.2 ShutdownOnSuccess示例

ShutdownOnFailure不同的是,前者会等所有的subtask结束后待待进行汇总,而ShutdownOnSuccess只需要一个subtask成功即会返回这个subtask的结果。

比如想要进行list sort排序,我们可以启用两个subtask,实现两个sort,这样只需要最快的那个返回,即视作成功。代码如下:

csharp 复制代码
public String getResources() throws InterruptedException, ExecutionException {
    try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
        scope.fork(() -> sort1());
        scope.fork(() -> sort2());

        scope.join();
        return scope.result();
    }
}

public static String sort1() throws InterruptedException {
    Thread.sleep(2000L); // mock IO cost
    return "sort1";
}

public static String sort2() throws InterruptedException {
    Thread.sleep(1000L); // mock IO cost
    return "sort2";
}

getResources()结果打印:sort2。

相关推荐
FoldWinCard10 分钟前
Python 第三次作业
java·服务器·python
傻啦嘿哟13 分钟前
Python列表排序:用key参数掌控排序规则
java·开发语言
大尚来也29 分钟前
解决 IDEA 运行 Spring Boot 测试时“命令行过长”错误的终极方案
java·spring boot·intellij-idea
云姜.32 分钟前
如何在idea上使用数据库
java·数据库·intellij-idea
一人の梅雨1 小时前
京东工业平台商品详情接口进阶实战:B2B采购场景适配+合规落地+多规格解析全方案
java·大数据·人工智能
callJJ1 小时前
Spring AI 语音合成(TTS)完全指南:OpenAI Text-to-Speech
java·人工智能·spring·语音识别·spring ai
重生之后端学习1 小时前
98. 验证二叉搜索树
java·数据结构·后端·算法·职场和发展
李慕婉学姐1 小时前
【开题答辩过程】以《智能小区物业管理系统设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·数据库·后端
m***06681 小时前
Spring Framework 中文官方文档
java·后端·spring
黎雁·泠崖1 小时前
【魔法森林冒险】13/14 支线任务 & 计分系统:丰富性与结局
java·开发语言