JUC之结构化并发编程(Java24 JEP499)

文章目录

  • 一、并发编程概述
    • [1.1 传统并发编程的痛点](#1.1 传统并发编程的痛点)
    • [1.2 结构化并发的核心理念](#1.2 结构化并发的核心理念)
  • 二、结构化并发编程详解
    • [2.1 核心概念与设计哲学](#2.1 核心概念与设计哲学)
    • [2.2 核心API](#2.2 核心API)
      • [2.2.1 StructuredTaskScope 类](#2.2.1 StructuredTaskScope 类)
      • [2.2.2 Task Scope](#2.2.2 Task Scope)
      • [2.2.3 共享数据机制](#2.2.3 共享数据机制)
      • [2.2.3 线程管理](#2.2.3 线程管理)
    • [2.3 结构化并发的内存模型与线程安全](#2.3 结构化并发的内存模型与线程安全)
    • [2.4 基础使用流程](#2.4 基础使用流程)
  • 三、结构化并发编程使用示例
    • [3.1 基本用法](#3.1 基本用法)
    • [3.2 错误传播与取消](#3.2 错误传播与取消)
    • [3.3 嵌套作用域](#3.3 嵌套作用域)
    • [3.4 处理订单](#3.4 处理订单)
  • 四、结构化并发的两种预定义策略
    • [4.1 ShutdownOnFailure](#4.1 ShutdownOnFailure)
    • [4.2 ShutdownOnSuccess](#4.2 ShutdownOnSuccess)
  • 五、结构化并发的实际应用场景
    • [5.1 电商订单处理](#5.1 电商订单处理)
    • [5.2 并行数据聚合(ShutdownOnFailure)](#5.2 并行数据聚合(ShutdownOnFailure))
    • [5.3 最快响应优先(ShutdownOnSuccess)](#5.3 最快响应优先(ShutdownOnSuccess))
    • [5.4 带超时的并发任务](#5.4 带超时的并发任务)
    • [5.5 使用 ScopedValue 共享上下文](#5.5 使用 ScopedValue 共享上下文)
    • [5.6 嵌套结构化并发](#5.6 嵌套结构化并发)
    • [5.7 用户信息聚合服务](#5.7 用户信息聚合服务)
    • [5.8 多镜像文件下载](#5.8 多镜像文件下载)
    • [5.9 批量数据处理管道](#5.9 批量数据处理管道)
  • 六、结构化并发的生命周期管理
  • 八、最佳实践总结
    • [8.1 任务范围总结](#8.1 任务范围总结)
    • [8.2 线程选择策略](#8.2 线程选择策略)
    • [8.3 异常处理最佳实践](#8.3 异常处理最佳实践)
    • [8.4 性能优化建议](#8.4 性能优化建议)
    • [8.5 代码组织原则](#8.5 代码组织原则)
    • [8.6 与传统并发方式的对比](#8.6 与传统并发方式的对比)

一、并发编程概述

1.1 传统并发编程的痛点

在 Java 21 之前,开发者通常使用以下方式处理并发任务:

  • 直接创建线程Thread 类)
  • 线程池管理ExecutorService
  • 异步结果处理Future

传统并发模型的问题

  1. 生命周期管理复杂
    • 线程的启动、阻塞、终止需要手动控制。
    • 容易出现线程泄漏(未正确关闭线程)。
  2. 异常处理困难
    • 异常可能在子线程中抛出,主线程无法直接捕获。
    • 多个任务的异常需要逐个检查,逻辑复杂。
  3. 资源泄漏风险高
    • 如果未正确关闭线程池或释放资源,可能导致内存泄漏。
  4. 代码可读性差
    • 并发逻辑分散在代码中,难以追踪任务依赖关系。

1.2 结构化并发的核心理念

Java 21 引入 结构化并发(Structured Concurrency),通过以下核心设计解决上述问题:

  1. 任务与子任务显式关联
    • 子任务的生命周期由父任务控制,父任务结束时子任务自动终止。
  2. 错误传播自动化
    • 子任务的异常会自动传播到父任务,无需手动检查。
  3. 资源管理简化
    • 使用 try-with-resources 自动释放资源(如线程、锁)。
  4. 代码结构清晰
    • 并发逻辑通过嵌套代码块体现,任务层次结构一目了然。

传统并发问题 任务泄漏: 线程未正确关闭 取消困难: 父任务取消后子任务继续运行 异常处理复杂: 多个任务异常难以协调 结果聚合繁琐: 多任务结果收集效率低 结构化并发: 任务与代码块生命周期绑定 结构化并发: 取消自动传播 结构化并发: 集中式异常处理 结构化并发: 简化结果聚合

结构化并发的核心原则:并发任务的生命周期应该被限制在明确定义的代码块中,形成可预测的层次结构

二、结构化并发编程详解

2.1 核心概念与设计哲学

结构化并发是一种编程范式,它将并发任务的执行生命周期限制在明确的语法结构中。在 Java 21 中,这通过 java.util.concurrent 包中的新 API 实现,目前仍处于预览阶段。

当前最新版本为Java26, 本机测试环境为Java24 GA版本.

结构化并发编程处理于第四个预览版本.

JEP 499: Structured Concurrency (Fourth Preview)

java 复制代码
https://openjdk.org/jeps/499

设计原则:

  1. 生命周期绑定:子任务的生命周期不能超过其父任务的作用域
  2. 错误传播:子任务中的异常能够可靠地传播到父任务
  3. 取消支持:父任务的取消会自动传播到所有子任务
  4. 可观测性:线程转储和调试信息能清晰反映任务间的层次关系

父任务/主线程 创建 StructuredTaskScope 子任务 1 子任务 2 子任务 3 等待所有子任务完成 处理结果

graph TD A[父任务] -->|创建| B[子任务1] A -->|创建| C[子任务2] A -->|创建| D[子任务3] B -->|完成| E[结果1] C -->|失败| F[异常] D -->|完成| G[结果3] A -- join() --> H[等待所有任务完成] H -- throwIfFailed() --> I[检查异常] I -- 无异常 --> J[继续执行] I -- 有异常 --> K[抛出异常]

2.2 核心API

2.2.1 StructuredTaskScope 类

这是结构化并发的核心类,管理一组子任务的执行和生命周期。

java 复制代码
@PreviewFeature(feature = PreviewFeature.Feature.STRUCTURED_CONCURRENCY)
public class StructuredTaskScope<T> implements AutoCloseable {
    private final ThreadFactory factory;
    private final ThreadFlock flock;
    private final ReentrantLock shutdownLock = new ReentrantLock();
	// ... 
}

Java 24 的结构化并发核心是 java.util.concurrent.StructuredTaskScope 类,其主要功能包括:

  • 创建和管理子任务
  • 协调多个并发操作
  • 处理任务结果和异常
  • 确保资源正确释放

核心类和方法

类/方法 说明
StructuredTaskScope<T> 管理一组并发任务的作用域
fork(Callable<T>) 在作用域内启动一个子任务
join() 阻塞当前线程,等待所有子任务完成
throwIfFailed() 如果任何任务失败,抛出异常
resultNow() 获取任务结果(需确保任务已成功完成)

2.2.2 Task Scope

  • StructuredTaskScope: 基础任务范围类,管理一组相关任务
    • ShutdownOnSuccess<T>:任一任务成功完成后关闭其他任务
    • ShutdownOnFailure:任一任务失败后关闭其他任务

2.2.3 共享数据机制

  • ScopedValue:在任务范围内安全共享不可变数据,替代 ThreadLocal

2.2.3 线程管理

  • 与虚拟线程(Virtual Threads)无缝集成,推荐配合使用

2.3 结构化并发的内存模型与线程安全

内存模型设计

  • 状态变更 :通过 AtomicReference 保证原子性(如 OPENSHUTDOWN)。
  • 任务列表 :使用 ConcurrentLinkedQueue 实现无锁并发。
java 复制代码
class StructuredTaskScope<T> {
    private final AtomicReference<State> state; // 状态管理
    private final ConcurrentLinkedQueue<Future<T>> futures; // 任务队列
    private final ThreadFactory factory; // 线程工厂

    enum State { OPEN, CLOSING, SHUTDOWN }

    public Future<U> fork(Callable<? extends U> task) {
        ensureOwnerAndState(); // 内存屏障
        Future<U> future = new FutureTask<>(task);
        futures.add(future);
        factory.newThread(future).start();
        return future;
    }
}

2.4 基础使用流程

使用结构化并发的标准流程可归纳为四步:

  1. 创建任务范围:使用 try-with-resources 确保自动关闭
  2. 提交子任务:通过 fork () 方法提交任务并获取 Future
  3. 等待任务完成:调用 join () 等待所有任务完成或终止
  4. 处理结果 / 异常:根据任务范围类型处理结果或异常
java 复制代码
// 结构化并发标准模板
try (var scope = new StructuredTaskScope.XXX()) {  // 1. 创建任务范围
    // 2. 提交子任务
    Future<T1> f1 = scope.fork(task1);
    Future<T2> f2 = scope.fork(task2);
    
    scope.join();  // 3. 等待任务完成
    
    // 4. 处理结果/异常
    scope.throwIfFailed();  // 或获取result
    T1 r1 = f1.resultNow();
    T2 r2 = f2.resultNow();
}

三、结构化并发编程使用示例

3.1 基本用法

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;

/**
 * @author: laoren
 * @description: TODO
 * @version: 1.0.0
 */
public class BasicStructuredTaskScopeDemo01 {
    public static void main(String[] args) {
        // ShutdownOnFailure: 创建一个 StructuredTaskScope.ShutdownOnFailure 对象,用于管理任务
        // 任一任务失败后关闭其他任务
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {

            // fork 方法创建一个任务,并返回一个 Future 对象
            var task1 = scope.fork(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }

                return "task 1 result!";
            });

            // fork 方法创建一个任务,并返回一个 Future 对象
            var task2 = scope.fork(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(2000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }

                return "task 2 result!";
            });

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

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

            // 获取任务结果
            System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), "result1 = " + task1.get());
            System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), "result2 = " + task2.get());
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
    }
}

关键点

  • 使用 try-with-resources 自动释放资源(关闭 StructuredTaskScope)。
  • ShutdownOnFailure 策略:任一任务失败时,其他任务会被取消。
  • join()throwIfFailed() 配合使用,确保所有任务成功完成后再继续执行。

3.2 错误传播与取消

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;

/**
 * @author: laoren
 * @description: 错误传播与取消操作
 * @version: 1.0.0
 */
public class ErrorHandlingDemo {
    static void main() {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            StructuredTaskScope.Subtask<String> task1 = scope.fork(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                return "task 1 result!";
            });

            var task2 = scope.fork(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                throw new RuntimeException("task 2 error!");
            });

            scope.join();
            // 抛出 ExecutionException,包含 task2 的异常
            scope.throwIfFailed();

            System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), " result1 = " + task1.get());
            System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), " result2 = " + task2.get());
        } catch (InterruptedException | ExecutionException e) {
            System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), " error occurred: " + e.getCause().getMessage());
        }
    }
}

关键点

  • throwIfFailed() 会抛出第一个失败任务的异常(包装在 ExecutionException 中)。
  • ShutdownOnFailure 会取消所有未完成的任务(如 task1)。
java 复制代码
public void throwIfFailed() throws ExecutionException {
    throwIfFailed(ExecutionException::new);
}
java 复制代码
public class ExecutionException extends Exception {}

3.3 嵌套作用域

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;

/**
 * @author: laoren
 * @description: 嵌套作用域测试
 * @version: 1.0.0
 */
public class NestedScopeDemo {

    static void main() {
        try (var outerScope = new StructuredTaskScope.ShutdownOnFailure()) {
            var outerTask = outerScope.fork(() -> {
                try (var innerScope = new StructuredTaskScope.ShutdownOnFailure()) {
                    var innerTask1 = innerScope.fork(() -> {
                        try {
                            TimeUnit.MILLISECONDS.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        return "inner task 1";
                    });
                    innerScope.join();
                    innerScope.throwIfFailed();
                    return "outer task 1";
                }
            });

            outerScope.join();
            outerScope.throwIfFailed();
            System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), " outer result = " + outerTask.get());
        } catch (ExecutionException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

关键点

  • 嵌套作用域(innerScope)的生命周期由外层作用域(outerScope)管理。
  • 如果 innerScope 中的任务失败,outerScope 会自动取消所有子任务。

3.4 处理订单

java 复制代码
package cn.tcmeta.usestructuredtaskscope;
// 编译和运行需添加 --enable-preview 参数
import java.util.concurrent.*;

public class StructuredConcurrencyDemo {

    // 模拟一个可能失败的服务调用
    private static String mockServiceCall(String serviceName, int delay, boolean shouldFail) 
        throws InterruptedException {
        Thread.sleep(delay);
        if (shouldFail) {
            throw new RuntimeException(serviceName + " failed intentionally");
        }
        return serviceName + " result after " + delay + "ms";
    }

    public static void main(String[] args) {
        try {
            String result = fetchUserDataParallel();
            System.out.println("Final result: " + result);
        } catch (Exception e) {
            System.out.println("Operation failed: " + e.getMessage());
        }
    }
    
    public static String fetchUserDataParallel() throws ExecutionException, InterruptedException {
        // 使用 try-with-resources 确保作用域正确关闭
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            
            // 并行启动三个子任务
            StructuredTaskScope.Subtask<String> userInfoTask = scope.fork(() ->
                mockServiceCall("UserService", 100, false));
                
            StructuredTaskScope.Subtask<String> orderHistoryTask = scope.fork(() ->
                mockServiceCall("OrderService", 200, false));
                
            StructuredTaskScope.Subtask<String> recommendationsTask = scope.fork(() ->
                mockServiceCall("RecommendationService", 150, true)); // 这个会失败

            // 等待所有任务完成或任何一个失败
            scope.join();
            
            // 如果任何任务失败,抛出异常
            scope.throwIfFailed();
            
            // 所有任务都成功,组合结果
            return String.format("User: %s, Orders: %s, Recomm: %s",
                userInfoTask.get(),
                orderHistoryTask.get(),
                recommendationsTask.get());
                
        } // 作用域结束,自动确保所有子任务完成或取消
    }
}

四、结构化并发的两种预定义策略

4.1 ShutdownOnFailure

  • 行为:任一子任务失败时,取消所有未完成的任务。
  • 适用场景:需要保证原子性的操作(如数据库事务)
java 复制代码
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    var task1 = scope.fork(() -> {
        Thread.sleep(1000);
        return "Task 1";
    });
    var task2 = scope.fork(() -> {
        Thread.sleep(500);
        throw new RuntimeException("Task 2 Failed");
    });

    scope.join();
    scope.throwIfFailed();
}

4.2 ShutdownOnSuccess

  • 行为:任一子任务成功完成时,取消所有未完成的任务。
  • 适用场景:需要找出最快完成任务的场景(如缓存预加载)。
java 复制代码
try (var scope = new StructuredTaskScope.ShutdownOnSuccess()) {
    var task1 = scope.fork(() -> {
        Thread.sleep(1000);
        return "Task 1";
    });
    var task2 = scope.fork(() -> {
        Thread.sleep(500);
        return "Task 2";
    });

    scope.join();
    scope.throwIfFailed();

    System.out.println("Fastest result: " + scope.results().get(0).get());
}
java 复制代码
Fastest result: Task 2

五、结构化并发的实际应用场景

5.1 电商订单处理

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;

/**
 * @author: laoren
 * @description: 订单流程处理
 * @version: 1.0.0
 */
public class OrderProcessing {
    static void main() {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            var inventoryCheck = scope.fork(() -> {
                System.out.println("Checking inventory...");
                Thread.sleep(1000);
                return true;
            });
            var paymentVerification = scope.fork(() -> {
                System.out.println("Verifying payment...");
                Thread.sleep(1500);
                return true;
            });
            var logRecording = scope.fork(() -> {
                System.out.println("Recording order log...");
                Thread.sleep(500);
                return true;
            });

            scope.join();
            scope.throwIfFailed();

            if(inventoryCheck.get() && paymentVerification.get() && logRecording.get()){
                System.out.println("Order processed successfully.");
            }
        } catch (ExecutionException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

5.2 并行数据聚合(ShutdownOnFailure)

适用于需要多个并行任务都成功完成才能得到最终结果的场景,如聚合多个服务的数据。

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.Future;
import java.time.LocalDateTime;

/**
 * 并行获取用户信息、订单列表和推荐商品,聚合后返回
 */
public class DataAggregationDemo {
     static void main(String[] args) {
        try {
            UserDashboard dashboard = fetchUserDashboard("1756866565");
            System.out.println("用户仪表盘数据:\n" + dashboard);
        } catch (Exception e) {
            System.err.println("获取仪表盘数据失败: " + e.getMessage());
        }
    }
    
    record UserDashboard(User user, OrderList orders, Recommendations recommendations) {}
    record User(String id, String name, String email) {}
    record OrderList(String userId, int count, LocalDateTime lastOrderTime) {}
    record Recommendations(String[] items) {}
    
    public static UserDashboard fetchUserDashboard(String userId) throws Exception {
        // 创建任务范围:任一任务失败则关闭其他任务
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 并行提交三个任务
            StructuredTaskScope.Subtask<User> userFuture = scope.fork(() -> fetchUser(userId));
            StructuredTaskScope.Subtask<OrderList> ordersFuture = scope.fork(() -> fetchOrders(userId));
            StructuredTaskScope.Subtask<Recommendations> recommendationsFuture = scope.fork(() -> fetchRecommendations(userId));
            
            // 等待所有任务完成或任一任务失败
            scope.join();
            
            // 检查是否有任务失败,有则抛出异常
            scope.throwIfFailed();
            
            // 所有任务成功,聚合结果
            return new UserDashboard(
                userFuture.get(),
                ordersFuture.get(),
                recommendationsFuture.get()
            );
        }
    }
    
    // 模拟获取用户信息
    private static User fetchUser(String userId) throws InterruptedException {
        Thread.sleep(300); // 模拟网络延迟
        return new User(userId, "老任", "1756866565@qq.com");
    }
    
    // 模拟获取订单列表
    private static OrderList fetchOrders(String userId) throws InterruptedException {
        Thread.sleep(500); // 模拟网络延迟
        return new OrderList(userId, 5, LocalDateTime.now().minusDays(2));
    }
    
    // 模拟获取推荐商品
    private static Recommendations fetchRecommendations(String userId) throws InterruptedException {
        Thread.sleep(400); // 模拟网络延迟
        return new Recommendations(new String[]{"商品A", "商品B", "商品C"});
    }
}
java 复制代码
用户仪表盘数据:
UserDashboard[user=User[id=tcmeta, name=老任, email=1756866565@qq.com], orders=OrderList[userId=tcmeta, count=5, lastOrderTime=2025-08-20T13:21:53.907171700], recommendations=Recommendations[items=[Ljava.lang.String;@4769b07b]]

流程解析

  • 三个任务并行执行,总耗时约等于最慢任务的时间(~500ms)
  • 任一任务失败会立即取消其他任务,避免资源浪费
  • 结果在所有任务成功后统一聚合,保证数据一致性

5.3 最快响应优先(ShutdownOnSuccess)

适用于从多个数据源获取相同数据,只需最快响应结果的场景,如多 CDN 节点内容获取。

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.ThreadFactory;
import java.net.URI;

/**
 * 从多个镜像服务器获取配置,使用第一个成功返回的结果
 */
public class FastestResponseDemo {
     static void main(String[] args) {
        String[] mirrorUrls = {
            "https://mirror1.example.com/config",
            "https://mirror2.example.com/config",
            "https://mirror3.example.com/config"
        };
        
        try {
            String config = fetchConfigFromFastestMirror(mirrorUrls);
            System.out.println("使用最快镜像的配置:\n" + config);
        } catch (Exception e) {
            System.err.println("所有镜像获取配置失败: " + e.getMessage());
        }
    }
    
    public static String fetchConfigFromFastestMirror(String[] urls) throws Exception {
        // 创建虚拟线程工厂,提高效率
        ThreadFactory vtf = Thread.ofVirtual().name("mirror-fetcher-", 0).factory();
        
        // 创建任务范围:任一任务成功则关闭其他任务
        try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>(null, vtf)) {
            // 向所有镜像提交获取任务
            for (String url : urls) {
                String u = url; // 有效final变量
                scope.fork(() -> fetchFromMirror(u));
            }
            
            // 等待任一任务成功或所有任务失败
            scope.join();
            
            // 返回第一个成功的结果
            return scope.result();
        }
    }
    
    // 从单个镜像获取配置
    private static String fetchFromMirror(String url) throws Exception {
        // 模拟不同镜像的响应延迟(100-1000ms)
        long delay = (long) (Math.random() * 900 + 100);
        Thread.sleep(delay);
        
        // 模拟偶然失败(10%概率)
        if (Math.random() < 0.1) {
            throw new RuntimeException("镜像 " + url + " 暂时不可用");
        }
        
        System.out.printf("线程名称: 【%s】 , msg: %s \n", Thread.currentThread().getName(), "镜像 " + url + " 响应成功,耗时 " + delay + "ms");
        return "配置内容(来自 " + url + ")";
    }
}

流程解析

  • 同时向多个镜像发送请求,谁先返回就用谁的结果
  • 第一个成功返回后,自动取消其他所有请求
  • 配合虚拟线程,高效处理多个网络请求

5.4 带超时的并发任务

结合超时机制,确保并发任务不会无限期等待,适用于对响应时间有严格要求的场景。

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 带超时的并发任务处理,确保在指定时间内完成
 */
public class TimeoutDemo {
    static void main(String[] args) {
        try {
            String result = processWithTimeout();
            System.out.println("任务处理结果: " + result);
        } catch (TimeoutException e) {
            System.err.println("任务超时: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("任务失败: " + e.getMessage());
        }
    }

    public static String processWithTimeout() throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 提交主任务
            var mainTask = scope.fork(() -> {
                Thread.sleep(800); // 模拟处理时间
                return "主任务结果";
            });

            // 提交监控任务(超时检查)
            var timeoutTask = scope.fork(() -> {
                Thread.sleep(500); // 超时阈值
                throw new TimeoutException("任务处理超时(>500ms)");
            });

            // 等待任一任务完成(主任务完成或超时)
            scope.join();

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

            // 返回主任务结果
            return mainTask.get();
        }
    }
}

流程解析

  • 同时运行主任务和超时监控任务
  • 若主任务在超时前完成,超时任务会被取消
  • 若超时时间到主任务仍未完成,超时任务抛出异常终止整个范围

5.5 使用 ScopedValue 共享上下文

在结构化并发中安全共享上下文信息(如用户认证信息、追踪 ID 等),替代 ThreadLocal。

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import javax.security.auth.callback.Callback;
import java.lang.ScopedValue;
import java.util.concurrent.Callable;
import java.util.concurrent.StructuredTaskScope;

/**
 * 使用ScopedValue在结构化并发任务间共享上下文
 */
public class ScopedValueDemo {
    // 定义可在范围内共享的值
    private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
    private static final ScopedValue<String> TRACE_ID = ScopedValue.newInstance();

    public static void main(String[] args) {
        // 设置共享上下文并执行任务
        String userId = "user-123";
        String traceId = "trace-" + System.currentTimeMillis();

        // 绑定多个ScopedValue
        ScopedValue.where(USER_ID, userId)
                .where(TRACE_ID, traceId)
                .run(() -> {
                    try {
                        processUserRequest();
                    } catch (Exception e) {
                        System.err.println("处理请求失败: " + e.getMessage());
                    }
                });
    }

    public static void processUserRequest() throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 提交需要访问上下文的任务
            StructuredTaskScope.Subtask<Callable<String>> task1 
                = scope.fork(ScopedValueDemo::logAccess);
            
            StructuredTaskScope.Subtask<Callable<String>> task2 		 =scope.fork(ScopedValueDemo::updateUserStatus);
           
            StructuredTaskScope.Subtask<Callable<String>> task3 = scope.fork(ScopedValueDemo::recordActivity);
            
            scope.join();
            scope.throwIfFailed();
            System.out.println("所有任务完成,上下文共享成功");
        }
    }

    private static Callable<String> logAccess() {
        // 访问共享的上下文值
        System.out.println("[" + TRACE_ID.get() + "] 记录访问 - 用户: " + USER_ID.get());
        return () -> "logAccess!";
    }

    private static Callable<String> updateUserStatus() {
        System.out.println("[" + TRACE_ID.get() + "] 更新状态 - 用户: " + USER_ID.get());
        return () -> "updateUserStatus!";
    }

    private static Callable<String> recordActivity() {
        System.out.println("[" + TRACE_ID.get() + "] 记录活动 - 用户: " + USER_ID.get());
        return () -> "updateUserStatus!";
    }
}

ScopedValue 优势

  • 不可变性:确保共享数据不会被意外修改
  • 范围绑定:超出范围自动失效,无内存泄漏风险
  • 继承性:子任务自动继承父任务的 ScopedValue
  • 性能:比 ThreadLocal 更高效,尤其在虚拟线程环境

5.6 嵌套结构化并发

创建任务的层次结构,实现复杂的并发工作流,如先并行获取数据,再并行处理数据

java 复制代码
package cn.tcmeta.usestructuredtaskscope;

import java.util.concurrent.Future;
import java.util.concurrent.StructuredTaskScope;

/**
 * @author: laoren
 * @description: 嵌套结构化并发
 * @version: 1.0.0
 */
public class NestedStructuredConcurrencyDemo {
    public static void main(String[] args) {
        try {
            // 顶层任务:处理订单
            OrderProcessingResult result = processOrder("ORDER-789");
            System.out.println("订单处理结果: " + result);
        } catch (Exception e) {
            System.err.println("订单处理失败: " + e.getMessage());
        }
    }

    record OrderProcessingResult(PaymentResult payment, InventoryResult inventory, ShippingLabel label) {}
    record PaymentResult(boolean success, String transactionId) {}
    record InventoryResult(boolean available, int quantity) {}
    record ShippingLabel(String labelId, String address) {}

    // 顶层任务:处理订单
    public static OrderProcessingResult processOrder(String orderId) throws Exception {
        try (var orderScope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 并行执行三大步骤
            StructuredTaskScope.Subtask<PaymentResult> paymentFuture = orderScope.fork(() -> processPayment(orderId));
            StructuredTaskScope.Subtask<InventoryResult> inventoryFuture = orderScope.fork(() -> checkInventory(orderId));
            StructuredTaskScope.Subtask<ShippingLabel> shippingFuture = orderScope.fork(() -> createShippingLabel(orderId));

            orderScope.join();
            orderScope.throwIfFailed();

            return new OrderProcessingResult(
                    paymentFuture.get(),
                    inventoryFuture.get(),
                    shippingFuture.get()
            );
        }
    }

    // 子任务1:处理支付(包含嵌套的结构化并发)
    private static PaymentResult processPayment(String orderId) throws Exception {
        // 嵌套的任务范围
        try (var paymentScope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 并行验证支付方式和处理支付
            StructuredTaskScope.Subtask<Boolean> validationFuture = paymentScope.fork(() -> validatePaymentMethod(orderId));
            StructuredTaskScope.Subtask<String> transactionFuture = paymentScope.fork(() -> processTransaction(orderId));

            paymentScope.join();
            paymentScope.throwIfFailed();

            return new PaymentResult(validationFuture.get(), transactionFuture.get());
        }
    }

    // 子任务2:检查库存
    private static InventoryResult checkInventory(String orderId) throws InterruptedException {
        Thread.sleep(300);
        return new InventoryResult(true, 10);
    }

    // 子任务3:创建运输标签
    private static ShippingLabel createShippingLabel(String orderId) throws InterruptedException {
        Thread.sleep(400);
        return new ShippingLabel("LABEL-" + orderId, "北京市海淀区...");
    }

    // 嵌套任务:验证支付方式
    private static boolean validatePaymentMethod(String orderId) throws InterruptedException {
        Thread.sleep(200);
        return true;
    }

    // 嵌套任务:处理交易
    private static String processTransaction(String orderId) throws InterruptedException {
        Thread.sleep(350);
        return "TXN-" + System.currentTimeMillis();
    }
}
java 复制代码
订单处理结果: OrderProcessingResult[payment=PaymentResult[success=true, transactionId=TXN-1755851931388], inventory=InventoryResult[available=true, quantity=10], label=ShippingLabel[labelId=LABEL-ORDER-789, address=北京市海淀区...]]

嵌套优势

  • 形成清晰的任务层次结构,与业务逻辑匹配
  • 取消操作会沿着层次结构传播,确保整体一致性
  • 异常处理可以在不同层级分别处理,符合责任链模式

5.7 用户信息聚合服务

java 复制代码
public class UserInfoAggregator {
    
    record UserProfile(String basicInfo, 
                      List<Order> orders, 
                      List<Recommendation> recommendations,
                      SocialProfile socialProfile) {}
    
    public UserProfile getUserProfile(String userId) 
        throws ExecutionException, InterruptedException {
        
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            
            // 并行获取用户各类信息
            Subtask<String> basicInfoTask = scope.fork(() -> 
                userService.getBasicInfo(userId));
                
            Subtask<List<Order>> ordersTask = scope.fork(() -> 
                orderService.getUserOrders(userId));
                
            Subtask<List<Recommendation>> recommendationsTask = scope.fork(() -> 
                recommendationService.getRecommendations(userId));
                
            Subtask<SocialProfile> socialProfileTask = scope.fork(() -> 
                socialService.getSocialProfile(userId));

            // 等待所有任务完成
            scope.join();
            scope.throwIfFailed(e -> {
                // 转换异常为领域特定异常
                if (e instanceof ServiceUnavailableException) {
                    return new ProfileServiceException("Service unavailable", e);
                }
                return new ProfileServiceException("Failed to fetch user profile", e);
            });

            // 组合所有结果
            return new UserProfile(
                basicInfoTask.get(),
                ordersTask.get(),
                recommendationsTask.get(),
                socialProfileTask.get()
            );
        }
    }
}

5.8 多镜像文件下载

java 复制代码
public class FileDownloader {
    
    public byte[] downloadFile(String fileId, List<String> mirrors) 
        throws ExecutionException, InterruptedException {
        
        try (var scope = new StructuredTaskScope.ShutdownOnSuccess<byte[]>()) {
            
            // 同时从多个镜像下载
            for (String mirror : mirrors) {
                scope.fork(() -> downloadFromMirror(mirror, fileId));
            }
            
            scope.join();
            
            // 返回第一个成功下载的内容
            return scope.result();
        } catch (Exception e) {
            throw new DownloadException("All mirrors failed for file: " + fileId, e);
        }
    }
    
    private byte[] downloadFromMirror(String mirrorUrl, String fileId) {
        // 实现下载逻辑
        // 抛出异常如果下载失败
        return httpClient.download(mirrorUrl + "/" + fileId);
    }
}

5.9 批量数据处理管道

java 复制代码
public class DataProcessingPipeline {
    
    public ProcessingResult processBatch(List<DataRecord> batch) 
        throws ExecutionException, InterruptedException {
        
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            
            // 为每个记录创建处理任务
            List<Subtask<RecordResult>> tasks = batch.stream()
                .map(record -> scope.fork(() -> processRecord(record)))
                .collect(Collectors.toList());
            
            scope.join();
            scope.throwIfFailed();
            
            // 收集所有成功结果
            List<RecordResult> results = tasks.stream()
                .map(Subtask::get)
                .collect(Collectors.toList());
                
            return aggregateResults(results);
        }
    }
    
    private RecordResult processRecord(DataRecord record) {
        // 复杂的数据处理逻辑
        validate(record);
        transform(record);
        return enrich(record);
    }
}

六、结构化并发的生命周期管理

结构化并发的核心优势在于严格的生命周期管理,通过以下机制实现:

  1. 范围绑定 :所有任务在StructuredTaskScope的 try-with-resources 块中创建,确保自动关闭
  2. 取消传播:当一个任务失败或被取消,所有相关任务都会被自动取消
  3. 异常聚合:多个任务的异常会被聚合处理,避免遗漏

实例化StructuredTaskScope 调用fork()提交任务 调用join() 所有任务完成 任一任务失败/成功 取消剩余任务 所有任务终止 try-with-resources自动关闭 Created Active Joining AllCompleted Shutdown Canceling Closed
是 否 创建 StructuredTaskScope 使用 scope.fork 启动子任务 调用 scope.join 等待完成 是否有任务失败? 处理或抛出异常 收集所有任务结果 清理资源 返回最终结果

完整代码模板

java 复制代码
public ReturnType structuredConcurrencyTemplate() 
    throws ExecutionException, InterruptedException {
    
    // 1. 创建作用域(选择合适策略)
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        
        // 2. 启动所有子任务
        Subtask<Result1> task1 = scope.fork(() -> operation1());
        Subtask<Result2> task2 = scope.fork(() -> operation2());
        // ... 更多任务
        
        // 3. 等待所有任务完成
        scope.join();
        
        // 4. 检查并处理失败
        if (scope.state() == StructuredTaskScope.State.FAILED) {
            // 自定义异常处理逻辑
            handleFailure(scope);
        }
        
        // 5. 或者使用 throwIfFailed 自动传播异常
        scope.throwIfFailed(e -> 
            new CustomBusinessException("Operations failed", e));
        
        // 6. 收集并处理成功结果
        return combineResults(
            task1.get(),
            task2.get()
            // ... 其他任务结果
        );
        
    } // 7. 自动清理:确保所有子任务完成
}

八、最佳实践总结

8.1 任务范围总结

场景 推荐使用的范围类型 核心特点
所有任务必须成功 ShutdownOnFailure 任一失败则全部取消
只需一个成功结果 ShutdownOnSuccess 任一成功则取消其他
自定义关闭逻辑 扩展 StructuredTaskScope 实现自定义的终止策略

8.2 线程选择策略

  • I/O 密集型任务 :优先使用虚拟线程(Thread.ofVirtual().factory()
  • CPU 密集型任务:使用平台线程池,并行度不宜超过 CPU 核心数
  • 混合任务:根据主要工作类型选择,或使用默认线程工厂

8.3 异常处理最佳实践

  • 始终在join()后调用throwIfFailed()(ShutdownOnFailure)
  • 使用 try-catch 块集中处理所有任务可能抛出的异常
  • 子任务应抛出有意义的异常,便于上层处理
  • 避免在子任务中捕获异常后不重新抛出,导致故障无法传播

8.4 性能优化建议

  • 合理设置任务粒度:避免过细(任务调度开销大)或过粗(并行度不足)
  • 利用虚拟线程处理大量短期任务,减少资源消耗
  • 对长时间运行的任务,实现中断响应逻辑
  • 避免在join()前阻塞,充分利用并行性

8.5 代码组织原则

  • 每个StructuredTaskScope对应一个明确的业务功能
  • 使用嵌套结构表达任务间的依赖关系
  • 将相关任务的处理逻辑放在一起,提高可读性
  • 提取重复的并发模式为工具方法

8.6 与传统并发方式的对比

特性 结构化并发 传统并发(ExecutorService)
任务生命周期 与代码块绑定,自动管理 手动管理,易泄漏
取消机制 自动传播到所有相关任务 需要手动追踪并取消
异常处理 集中式处理,自动聚合 分散处理,需手动协调
代码可读性 高,结构清晰 低,分散在多处
调试难度 低,层次结构明确 高,线程关系复杂
资源效率 高,配合虚拟线程更佳 中,线程创建成本高
学习曲线 中等,需理解新模型 低,但深入难
相关推荐
十八旬5 分钟前
苍穹外卖项目实战(日记十)-记录实战教程及问题的解决方法-(day3-2)新增菜品功能完整版
java·开发语言·spring boot·mysql·idea·苍穹外卖
鞋尖的灰尘20 分钟前
springboot-事务
java·后端
银迢迢26 分钟前
SpringCloud微服务技术自用笔记
java·spring cloud·微服务·gateway·sentinel
用户03321266636733 分钟前
Java 将 CSV 转换为 Excel:告别繁琐,拥抱高效数据处理
java·excel
这周也會开心38 分钟前
Java-多态
java·开发语言
渣哥1 小时前
揭秘!Java反射机制到底是什么?原来应用场景这么广!
java
叫我阿柒啊1 小时前
Java全栈开发实战:从Spring Boot到Vue3的项目实践
java·spring boot·微服务·性能优化·vue3·全栈开发
CPU NULL1 小时前
Spring拦截器中@Resource注入为null的问题
java·人工智能·后端·spring
藤椒鱼不爱编程1 小时前
面向对象_抽象类与接口
java
MacroZheng1 小时前
告别Swagger UI!一款更适合Spring Boot的API文档新选择!
java·spring boot·后端