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)
任务生命周期 与代码块绑定,自动管理 手动管理,易泄漏
取消机制 自动传播到所有相关任务 需要手动追踪并取消
异常处理 集中式处理,自动聚合 分散处理,需手动协调
代码可读性 高,结构清晰 低,分散在多处
调试难度 低,层次结构明确 高,线程关系复杂
资源效率 高,配合虚拟线程更佳 中,线程创建成本高
学习曲线 中等,需理解新模型 低,但深入难
相关推荐
稚辉君.MCA_P8_Java1 分钟前
kafka解决了什么问题?mmap 和sendfile
java·spring boot·分布式·kafka·kubernetes
乄bluefox3 分钟前
保姆级docker部署nacos集群
java·docker·容器
欣然~10 分钟前
百度地图收藏地址提取与格式转换工具 说明文档
java·开发语言·dubbo
玩毛线的包子30 分钟前
Android Gradle学习(十三)- 配置读取和文件写入
java
青岛少儿编程-王老师42 分钟前
CCF编程能力等级认证GESP—C++6级—20250927
java·c++·算法
一條狗1 小时前
学习日报 20251007|深度解析:基于 Guava LoadingCache 的优惠券模板缓存设计与实现
java·oracle·loadingcache
Miraitowa_cheems2 小时前
LeetCode算法日记 - Day 64: 岛屿的最大面积、被围绕的区域
java·算法·leetcode·决策树·职场和发展·深度优先·推荐算法
Lisonseekpan2 小时前
Spring Boot 中使用 Caffeine 缓存详解与案例
java·spring boot·后端·spring·缓存
为java加瓦2 小时前
Rust 的类型自动解引用:隐藏在人体工学设计中的魔法
java·服务器·rust
SimonKing2 小时前
分布式日志排查太头疼?TLog 让你一眼看穿请求链路!
java·后端·程序员