一、结构化并发(Structured Concurrency)

     结构化并发是Java 21引入的预览特性,它通过将并发任务组织成一个有明确生命周期和作用域的结构,使并发代码更易于理解、维护和调试。它解决了传统异步编程中的几个关键问题:任务泄漏、错误传播和上下文传递。

  • 结构化任务作用域(StructuredTaskScope):定义并发任务的边界和生命周期
  • 子任务:在作用域内启动的并发任务
  • 作用域生命周期:作用域关闭时,所有子任务必须完成或被取消
  • 错误传播:子任务的异常会传播到父作用域

结构化并发”指的也是“父子结构”,即线程之间不是毫无关联,随意并发的,而是有清晰的父线程子线程抽象。线程 A 启动线程 B 后,A就是父线程,B就是子线程。结构化并发要求在父线程退出之前,所有子线程都要结束(成功完结或取消执行)

二、结构化并发与非结构化并发的技术实现对比

在Java中,结构化并发的实现主要依赖于新的API和语言特性,而非结构化并发则主要依赖传统的线程和线程池机制。

特性 结构化并发 非结构化并发
任务关系 父子结构明确 任务独立
生命周期管理 由框架统一管理 需手动管理
异常处理 统一捕获与传播 需单独处理
资源释放 自动释放 需手动释放
典型API Java 21 StructuredTaskScope ExecutorService、Thread

二、结构化并发的核心组件

作用域(Scopes)

      作用域是结构化并发的核心概念,用于管理一组任务的生命周期。通过 Scope 接口。

任务句柄(Task Handles)

      任务句柄代表异步执行的任务

异常处理策略

 结构化并发提供了多种异常处理模式:

  1. ShutdownOnFailure:任何任务失败立即终止作用域。

  2. ContinueOnFailure:允许任务继续执行,收集所有异常。

  3. CustomExceptionHandler:自定义异常处理逻辑。

  1. 主作用域管理‌:使用StructuredTaskScope创建并发任务组,确保所有任务作为一个工作单元执行
  2. 任务启动机制‌:通过fork()方法启动异步任务,返回Subtask对象用于结果获取
  3. 异常处理策略‌:通过throwIfFailed()统一处理任务失败情况,保证错误传播
  4. 资源自动清理‌:采用try-with-resources语法,确保作用域正确关闭
  5. 任务状态监控‌:提供任务状态查询和异常获取功能
import java.util.concurrent.StructuredTaskScope;

public class StructuredConcurrency1 {

    public static void main(String[] args) {
        // 在主作用域中执行并发任务
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

            // 创建子作用域并启动异步任务
            var task1 = scope.fork(() -> performTask("任务 1", 1000));
            var task2 = scope.fork(() -> performTask("任务 2", 2000));

            // 等待所有子任务完成
            scope.join();

            // 检查是否有任务失败
            scope.throwIfFailed();

            System.out.println("所有任务都已完成");
            // 获取任务结果
            System.out.println(task1.get()+" => "+task1.state());
            System.out.println(task2.get()+" => "+task1.state());

        }  catch (Exception e) {
            System.out.println("任务执行失败: " + e.getMessage());
        }
    }


    private static String performTask(String taskName, int duration) {
        try {
            System.out.println(taskName + " 开始执行");
            // 模拟任务执行时间
            Thread.sleep(duration);
            System.out.println(taskName + " 执行完成");
            return taskName ;
        } catch (InterruptedException e) {
            // 恢复中断状态,以便上层调用者可以知道发生了中断
            Thread.currentThread().interrupt();
            throw new RuntimeException(taskName + " 被中断", e);
        }
    }
}

三、结构化并发的高级应用技巧

  1. 结构化并发:确保所有fork的任务都在scope作用域内管理

  2. 失败策略:采用ShutdownOnFailure策略,任一任务失败即终止

  3. 状态检查:通过Subtask.State枚举检查任务执行状态

  4. 模拟现实:订单数据有70%概率随机失败

import java.util.List;
import java.util.concurrent.StructuredTaskScope;

public class AdvancedStructuredConcurrency {

    public static void main(String[] args) throws InterruptedException {

        // 使用失败处理策略
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

            // 启动多个并发任务
            List<StructuredTaskScope.Subtask<String>> tasks = List.of(
                    scope.fork(() -> fetchData("用户数据", 1000)),
                    scope.fork(() -> fetchData("订单数据", 900)),
                    scope.fork(() -> fetchData("库存数据", 1500))
            );

            // 等待所有任务完成
            scope.join();

            // 处理任务结果
            for (var task : tasks) {
                if (task.state() == StructuredTaskScope.Subtask.State.SUCCESS) {
                    System.out.println("成功获取: " + task.get());
                } else if (task.state() == StructuredTaskScope.Subtask.State.FAILED) {
                    System.out.println("任务失败: " + task.exception().getMessage());
                }
            }
    }
}


    private static String fetchData(String dataType, int timeout) {
        try {
            // 模拟数据获取
            Thread.sleep(timeout);
            double random = Math.random();
            System.out.println(random);

            if (dataType.contains("订单") && random > 0.7) {
                throw new RuntimeException(dataType + " 获取失败");
            }
            return dataType + " - 耗时 " + timeout + "ms";

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(dataType + " 获取被中断");
        }
    }
}

成功如图:

失败如图:

总之,结构化并发需要 JDK 21 及以上版本支持,但它已经展现出巨大的潜力。随着 Java 生态的持续优化,结构化并发将成为现代 Java 开发的标准实践。合理使用结构化并发,能够有效减少并发编程中的错误,提高系统的可靠性和性能。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐