admin管理员组

文章数量:1441139

Java 19 中的结构化并发

1. 概述

在本教程中,我们将讨论孵化器功能结构化并发 (JEP 428),它为 Java 19 提供了结构化并发功能。我们将指导你使用新的 API 来管理多线程代码。

2. 理念

通过采用并发编程风格来降低线程泄漏和取消延迟的可能性,从而增强多线程代码的可维护性、可靠性和可观察性,这是与取消和关闭相关的常见风险。为了更好地理解非结构化并发的问题,让我们看一个例子:

代码语言:javascript代码运行次数:0运行复制
Future<Shelter> shelter;
Future<List<Dog>> dogs;
try (ExecutorService executorService = Executors.newFixedThreadPool(3)) {
    shelter = executorService.submit(this::getShelter);
    dogs = executorService.submit(this::getDogs);
    Shelter theShelter = shelter.get();   // Join the shelter
    List<Dog> theDogs = dogs.get();  // Join the dogs
    Response response = new Response(theShelter, theDogs);
} catch (ExecutionException | InterruptedException e) {
    throw new RuntimeException(e);
}Copy

getShelter()运行时,代码不会注意到getDogs()是否可能失败,并且将继续不必要的阻塞shelter.get()调用的原因。因此,只有在getShelter()完成和getDogs()returns之后,dogs.get()才会抛出异常,我们的代码才会失败:

但这不是唯一的问题。当执行代码的线程被中断时,它不会将中断传播到我们的子任务。此外,如果第一个执行的子任务庇护所抛出异常,它不会被委派给狗的子任务,它会继续运行,浪费资源。

结构化并发试图解决这些问题,我们将在下一章中看到。

3. 示例

对于结构化并发示例,我们将使用以下记录:

代码语言:javascript代码运行次数:0运行复制
record Shelter(String name) { }

record Dog(String name) { }

record Response(Shelter shelter, List<Dog> dogs) { }Copy

我们还将提供两种方法。一个获得庇护所

代码语言:javascript代码运行次数:0运行复制
private Shelter getShelter() {
    return new Shelter("Shelter");
}Copy

另一种是检索Dog元素列表:

代码语言:javascript代码运行次数:0运行复制
private List<Dog> getDogs() {
    return List.of(new Dog("Buddy"), new Dog("Simba"));
}Copy

由于结构化并发是孵化器功能,因此我们必须使用以下参数运行应用程序:

代码语言:javascript代码运行次数:0运行复制
--enable-preview --add-modules jdk.incubator.foreignCopy

否则,我们可以添加一个模块信息.java并将包标记为必需。

让我们看一个例子:

代码语言:javascript代码运行次数:0运行复制
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<Shelter> shelter = scope.fork(this::getShelter);
    Future<List<Dog>> dogs = scope.fork(this::getDogs);
    scope.join();
    Response response = new Response(shelter.resultNow(), dogs.resultNow());
    // ...
}Copy

由于StructuredTaskScope实现了AutoCloseable接口,因此我们可以在 try-with-resources 语句中使用它。StructuredTaskScope为我们提供了两个子类,它们有不同的用途。在本教程中,我们将使用ShutdownOnFailure(),它会在出现问题时关闭子任务。

还有一个ShutdownOnSuccess() 构造函数,它的作用恰恰相反。如果成功,它会关闭子任务。这种短路模式有助于我们避免不必要的工作。

StructuredTaskScope的使用与同步代码的结构非常相似。创建范围的线程是所有者。作用域允许我们在作用域分叉其他子任务。此代码以异步方式调用。在join() 方法的帮助下,我们可以阻止所有任务,直到它们交付结果。

每个任务都可以借助作用域shutdown() 方法终止其他任务。throwIfFailed() 方法提供了另一种可能性:

代码语言:javascript代码运行次数:0运行复制
scope.throwIfFailed(e -> new RuntimeException("ERROR_MESSAGE"));
Copy

它允许我们在任何分叉失败时传播任何异常。此外,我们还可以设置一个截止日期加入直到

代码语言:javascript代码运行次数:0运行复制
scope.joinUntil(Instant.now().plusSeconds(1));
Copy

如果任务尚未完成,这将在时间到期后引发异常。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2023-02-22,如有侵权请联系 cloudcommunity@tencent 删除异常java并发教程线程

本文标签: Java 19 中的结构化并发