Java异步编程终极实战指南

这是一份非常详细、实用、通俗易懂、权威且全面的 Java 异步编程指南。内容涵盖从基础概念到高级应用,包含大量可直接运行的代码示例和完整的系统案例。


Java 异步编程全面指南

目录

  1. 理解异步编程

    • 1.1 同步 vs 异步
    • 1.2 阻塞 vs 非阻塞
    • 1.3 为什么需要异步编程?
    • 1.4 异步编程的核心挑战
  2. Java 异步编程基础

    • 2.1 Thread 与 Runnable
    • 2.2 Callable 与 Future
    • 2.3 ExecutorService 与线程池
      • 2.3.1 线程池原理与优势
      • 2.3.2 创建线程池 (ThreadPoolExecutor, Executors)
      • 2.3.3 线程池参数详解 (corePoolSize, maxPoolSize, keepAliveTime, queue, handler)
      • 2.3.4 最佳实践与常见陷阱
  3. Java 高级异步工具

    • 3.1 CompletableFuture (Java 8+)
      • 3.1.1 核心概念:Future 的增强
      • 3.1.2 创建 CompletableFuture (supplyAsync, runAsync)
      • 3.1.3 链式操作与组合 (thenApply, thenAccept, thenRun, thenCompose)
      • 3.1.4 组合多个 Future (thenCombine, thenAcceptBoth, runAfterBoth, applyToEither, acceptEither, runAfterEither)
      • 3.1.5 异常处理 (exceptionally, handle, whenComplete)
      • 3.1.6 等待多个任务完成 (allOf, anyOf)
      • 3.1.7 执行器控制 (*Async 方法的重载)
      • 3.1.8 超时控制 (orTimeout, completeOnTimeout)
      • 3.1.9 CompletableFuture 最佳实践
    • 3.2 Fork/Join 框架 (Java 7+)
      • 3.2.1 分治思想与工作窃取
      • 3.2.2 RecursiveTaskRecursiveAction
      • 3.2.3 ForkJoinPool
      • 3.2.4 适用场景与限制
  4. 响应式编程与 Reactive Streams

    • 4.1 响应式编程概念 (Reactive Programming)
      • 4.1.1 观察者模式、迭代器模式与数据流
      • 4.1.2 背压 (Backpressure)
    • 4.2 Reactive Streams 规范 (Publisher, Subscriber, Subscription, Processor)
    • 4.3 Project Reactor 实现
      • 4.3.1 核心类型:MonoFlux
      • 4.3.2 创建数据流 (just, fromIterable, fromStream, empty, error, create, defer, interval, fromFuture)
      • 4.3.3 操作符 (map, flatMap, filter, zip, reduce, collect, merge, concat, buffer, window, delayElements, onError*, retry*)
      • 4.3.4 调度器 (Schedulers) 与线程控制 (publishOn, subscribeOn)
      • 4.3.5 背压处理策略
      • 4.3.6 测试 (StepVerifier)
    • 4.4 其他响应式库简介 (RxJava, Akka Streams)
  5. 异步 IO 与 NIO

    • 5.1 Java NIO 基础 (Channel, Buffer, Selector)
    • 5.2 使用 AsynchronousFileChannel 进行异步文件操作
    • 5.3 使用 AsynchronousSocketChannel 进行异步网络通信
    • 5.4 CompletionHandler 接口
  6. 异步编程中的并发控制

    • 6.1 原子类 (AtomicInteger, AtomicLong, AtomicReference, LongAdder)
    • 6.2 锁 (ReentrantLock, StampedLock, ReadWriteLock)
    • 6.3 并发容器 (ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue)
    • 6.4 避免共享可变状态
  7. 异步编程最佳实践与性能调优

    • 7.1 选择合适的工具 (Threads, Futures, CompletableFuture, Reactive)
    • 7.2 线程池配置原则
    • 7.3 资源管理与泄漏预防 (try-with-resources, AutoCloseable)
    • 7.4 异常处理策略
    • 7.5 日志记录与调试技巧
    • 7.6 性能监控与分析工具 (JMX, Profilers)
    • 7.7 避免常见陷阱 (死锁、活锁、线程饥饿、回调地狱)
  8. 综合实战案例

    • 8.1 案例一:异步文件处理与上传
    • 8.2 案例二:异步微服务调用聚合
    • 8.3 案例三:响应式 Web 应用 (使用 Spring WebFlux)

1. 理解异步编程

  • 1.1 同步 vs 异步
    • 同步 (Synchronous): 调用者发出请求后,必须等待 被调用者完成整个操作并返回结果,才能继续执行后续代码。调用者线程在此期间处于阻塞状态。
    • 异步 (Asynchronous): 调用者发出请求后,无需等待被调用者完成操作,可以立即返回并继续执行后续代码。被调用者完成操作后,会通过某种机制(如回调函数、Future、消息通知)告知调用者结果。
    • 通俗比喻: 同步像在餐厅点餐后坐在座位上等服务员上菜;异步像点餐后拿到一个号牌,可以去干别的事(如看书),等菜好了服务员叫号通知你。
  • 1.2 阻塞 vs 非阻塞
    • 阻塞 (Blocking): 指调用结果返回之前,调用者线程会被挂起,无法执行其他任务。同步调用必然是阻塞的。
    • 非阻塞 (Non-blocking): 指调用后即使不能立即得到结果,调用者线程也不会被挂起,可以继续执行其他任务。异步调用通常是非阻塞的。
    • 关系: 同步/异步关注的是通信机制 (是否需要等待结果)。阻塞/非阻塞关注的是调用者线程的状态(在等待结果时是否被挂起)。它们常被一起讨论,但描述的是不同层面。
  • 1.3 为什么需要异步编程?
    • 提高吞吐量 (Throughput): 避免线程因等待 I/O(网络、磁盘)或长时间计算而空闲,让 CPU 去服务其他请求,最大化利用系统资源。特别是在高并发场景下。
    • 提高响应性 (Responsiveness): UI 应用避免主线程被阻塞导致界面卡顿;服务器应用能更快响应客户端请求。
    • 资源效率: 创建和切换线程开销大。异步模型(如事件循环、响应式)可以用更少的线程处理更多请求。
    • 简化并行: 更容易编写并行处理任务的代码。
  • 1.4 异步编程的核心挑战
    • 复杂性: 代码流程不再是线性的,理解和调试更困难。
    • 错误处理: 异常可能发生在不同的线程或回调中,传播和处理机制复杂。
    • 状态管理: 多个异步操作可能访问共享状态,需要小心处理并发问题(竞态条件)。
    • 回调地狱 (Callback Hell): 嵌套的回调函数使代码难以阅读和维护。CompletableFuture 和响应式编程旨在解决此问题。
    • 资源泄漏: 忘记关闭连接、释放资源可能导致泄漏。
    • 背压 (Backpressure): 当生产者速度远快于消费者时,如何防止消费者被压垮。Reactive Streams 规范定义了处理背压的标准。

2. Java 异步编程基础

  • 2.1 Thread 与 Runnable
    • 原理: Java 最基本的并发单元是线程 (Thread)。Runnable 接口定义了一个可以被线程执行的任务 (run() 方法)。

    • 示例:

      java 复制代码
      public class BasicThreadExample {
          public static void main(String[] args) {
              // 创建 Runnable 任务
              Runnable task = () -> {
                  String threadName = Thread.currentThread().getName();
                  System.out.println("Hello from " + threadName);
              };
              // 创建线程并启动
              Thread thread = new Thread(task);
              thread.start(); // 启动新线程执行任务
              System.out.println("Main thread done."); // 主线程继续执行
          }
      }
    • 缺点: 直接创建线程开销大,线程数量难以管理,缺乏任务执行结果和异常处理机制。

  • 2.2 Callable 与 Future
    • 原理: Callable 类似于 Runnable,但它可以返回一个结果 (call() 方法) 并抛出异常。Future 接口代表一个异步计算的结果。它提供方法来检查计算是否完成、等待计算完成以及检索计算结果。

    • 示例 (使用 ExecutorService):

      java 复制代码
      import java.util.concurrent.*;
      
      public class CallableFutureExample {
          public static void main(String[] args) throws ExecutionException, InterruptedException {
              ExecutorService executor = Executors.newSingleThreadExecutor();
              // 创建 Callable 任务
              Callable<Integer> task = () -> {
                  Thread.sleep(1000); // 模拟耗时操作
                  return 42;
              };
              // 提交任务,获取 Future
              Future<Integer> future = executor.submit(task);
              System.out.println("Main thread doing other work...");
              // 阻塞等待结果 (可选,也可以先做别的事,后面再 get)
              Integer result = future.get(); // 阻塞直到任务完成并返回结果
              System.out.println("Result: " + result);
              executor.shutdown(); // 关闭线程池
          }
      }
    • 优点: 相比直接 Thread,提供了任务结果和异常捕获的机制。

    • 缺点: Future.get() 是阻塞的。组合多个异步任务、链式调用比较繁琐。

  • 2.3 ExecutorService 与线程池
    • 2.3.1 线程池原理与优势

      • 原理: 预先创建一组线程并放入池中。当有任务提交时,从池中取出空闲线程执行。任务完成后,线程返回池中等待下次任务。避免了频繁创建和销毁线程的开销。
      • 优势:
        • 降低资源消耗: 重用已创建的线程。
        • 提高响应速度: 任务到达时线程已存在,无需等待创建。
        • 提高线程可管理性: 可统一管理线程池大小、任务队列、拒绝策略等。
    • 2.3.2 创建线程池

      • 工厂方法 (Executors):

        java 复制代码
        // 固定大小线程池
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
        // 单线程线程池 (保证任务顺序执行)
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        // 缓存线程池 (根据需要创建新线程,空闲线程存活 60 秒)
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        // 计划任务线程池 (可延迟或周期性执行任务)
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
      • 直接创建 (ThreadPoolExecutor): 提供更细粒度的控制。

        java 复制代码
        int corePoolSize = 5; // 核心线程数 (即使空闲也保留)
        int maxPoolSize = 10; // 最大线程数
        long keepAliveTime = 60L; // 非核心线程空闲存活时间 (秒)
        TimeUnit unit = TimeUnit.SECONDS; // 时间单位
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100); // 任务队列
        ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略
        ExecutorService executor = new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler);
    • 2.3.3 线程池参数详解

      • corePoolSize: 核心线程数。即使空闲也保留在线程池中(除非设置 allowCoreThreadTimeOut)。
      • maxPoolSize: 最大线程数。当任务队列满且线程数小于 maxPoolSize 时,会创建新线程。
      • keepAliveTime: 当线程数大于 corePoolSize 时,多余的空闲线程在终止前等待新任务的最长时间。
      • workQueue: 用于保存等待执行任务的阻塞队列。常见类型:
        • LinkedBlockingQueue: 无界队列(除非指定容量)。可能导致 OOM。
        • ArrayBlockingQueue: 有界队列。
        • SynchronousQueue: 不存储元素的队列。每个插入操作必须等待一个移除操作。
        • PriorityBlockingQueue: 具有优先级的无界队列。
      • threadFactory: 用于创建新线程的工厂。可以设置线程名、优先级、守护状态等。
      • handler: 当线程池饱和(线程数达到 maxPoolSize 且队列已满)时,新提交任务的处理策略(拒绝策略):
        • AbortPolicy (默认):抛出 RejectedExecutionException
        • CallerRunsPolicy: 由调用者线程(提交任务的线程)执行该任务。
        • DiscardPolicy: 静默丢弃新任务。
        • DiscardOldestPolicy: 丢弃队列中最老的任务,尝试重新提交新任务。
    • 2.3.4 最佳实践与常见陷阱

      • 最佳实践:
        • 使用 ThreadPoolExecutor 构造函数明确设置参数,避免 Executors 工厂方法创建无界队列导致 OOM。
        • 根据任务类型(CPU 密集型、IO 密集型)合理设置线程池大小。通常,CPU 密集型任务:N_{threads} = N_{CPU} + 1;IO 密集型任务:N_{threads} = N_{CPU} \\times U_{CPU} \\times (1 + W/C),其中 U_{CPU} 是目标 CPU 利用率(0 < U <= 1),W 是等待时间,C 是计算时间。实践中需测试调整。
        • 使用有界队列并设置合理的拒绝策略。
        • 在任务中正确处理中断 (InterruptedException)。
        • 使用 try-with-resources 或确保在应用关闭时调用 shutdown() / shutdownNow() 关闭线程池。
      • 常见陷阱:
        • 任务泄漏/线程泄漏: 忘记关闭线程池或未处理任务中的异常导致线程终止。
        • 资源耗尽: 无界队列导致 OOM;线程池过大导致上下文切换开销过高。
        • 死锁: 任务间相互等待对方持有的资源。
        • 性能问题: 线程池配置不当(大小、队列、拒绝策略)。

3. Java 高级异步工具

  • 3.1 CompletableFuture (Java 8+)
    • 3.1.1 核心概念:Future 的增强

      • CompletableFutureFuture 的增强版,实现了 FutureCompletionStage 接口。
      • 支持异步任务的链式组合非阻塞回调异常处理合并多个任务结果等。
      • 解决了 Future 的阻塞问题 (get()) 和组合多个任务的繁琐性。
    • 3.1.2 创建 CompletableFuture

      • CompletableFuture.supplyAsync(Supplier<U> supplier): 异步执行一个有返回值的任务。默认使用 ForkJoinPool.commonPool()
      • CompletableFuture.supplyAsync(Supplier<U> supplier, Executor executor): 使用指定的 Executor
      • CompletableFuture.runAsync(Runnable runnable): 异步执行一个无返回值的任务。
      • CompletableFuture.runAsync(Runnable runnable, Executor executor).
      • CompletableFuture.completedFuture(U value): 创建一个已经完成的 CompletableFuture,结果为给定值。
    • 3.1.3 链式操作与组合

      • thenApply(Function<? super T,? extends U> fn): 当当前阶段完成时,对其结果应用一个函数,并返回一个新的 CompletableFuture 代表转换后的结果。同步执行

      • thenApplyAsync(Function<? super T,? extends U> fn): 异步执行 fn。使用默认 ForkJoinPool

      • thenApplyAsync(Function<? super T,? extends U> fn, Executor executor): 使用指定的 Executor

      • thenAccept(Consumer<? super T> action): 消费结果,不返回新值。

      • thenRun(Runnable action): 执行一个动作,不消费结果也不返回新值。

      • thenCompose(Function<? super T, ? extends CompletionStage<U>> fn): 当当前阶段完成时,将其结果传递给另一个异步计算函数 (fn),并返回该函数返回的 CompletionStage。用于链式依赖的异步任务。例如:任务A完成后,其结果是任务B的输入。

      • 示例:

        java 复制代码
        import java.util.concurrent.CompletableFuture;
        import java.util.concurrent.ExecutionException;
        public class CompletableFutureChainExample {
            public static void main(String[] args) throws ExecutionException, InterruptedException {
                // 模拟异步获取用户ID
                CompletableFuture<String> userIdFuture = CompletableFuture.supplyAsync(() -> {
                    sleep(500); // 模拟耗时
                    return "user123";
                });
                // 链式调用: 获取用户ID -> 获取用户详情 -> 获取用户订单
                CompletableFuture<String> resultFuture = userIdFuture
                        .thenApply(userId -> {
                            System.out.println("Fetching details for " + userId);
                            sleep(300); // 模拟耗时
                            return "Details of " + userId;
                        })
                        .thenCompose(details -> {
                            System.out.println("Fetching orders based on details: " + details);
                            return CompletableFuture.supplyAsync(() -> {
                                sleep(400); // 模拟耗时
                                return "Orders for " + details;
                            });
                        });
                // 非阻塞获取最终结果 (注册回调)
                resultFuture.thenAccept(finalResult -> System.out.println("Final Result: " + finalResult));
                // 主线程继续做其他事情
                System.out.println("Main thread doing other work...");
                sleep(2000); // 等待异步任务完成
            }
            static void sleep(long millis) {
                try {
                    Thread.sleep(millis);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    • 3.1.4 组合多个 Future

      • thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn): 当当前另一个 (other) 阶段都完成时,将两个结果传递给 fn,返回新的 CompletableFuture 代表合并结果。

      • thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action): 两个都完成后,消费两个结果。

      • runAfterBoth(CompletionStage<?> other, Runnable action): 两个都完成后,执行一个动作。

      • applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn): 当前或 other 任意一个完成时,将其结果传递给 fn,返回新 CompletableFuture

      • acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action): 任意一个完成后,消费其结果。

      • runAfterEither(CompletionStage<?> other, Runnable action): 任意一个完成后,执行动作。

      • 示例 (thenCombine):

        java 复制代码
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            sleep(300); return 10;
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            sleep(200); return 20;
        });
        future1.thenCombine(future2, (result1, result2) -> result1 + result2)
               .thenAccept(sum -> System.out.println("Sum: " + sum));
    • 3.1.5 异常处理

      • exceptionally(Function<Throwable, ? extends T> fn): 当阶段异常完成时,使用 fn 处理异常并返回一个替代值。类似于 catch

      • handle(BiFunction<? super T, Throwable, ? extends U> fn): 无论阶段正常完成还是异常完成,都会调用 fnfn 接收结果或异常,并返回一个新结果。类似于 finally 里获取结果或异常。

      • whenComplete(BiConsumer<? super T, ? super Throwable> action): 无论阶段正常完成还是异常完成,都会调用 action 消费结果或异常。不改变结果值。用于日志记录等副作用操作。

      • 示例 (exceptionally):

        java 复制代码
        CompletableFuture.supplyAsync(() -> {
            if (Math.random() > 0.5) {
                throw new RuntimeException("Oops!");
            }
            return "Success";
        }).exceptionally(ex -> {
            System.err.println("Error: " + ex.getMessage());
            return "Recovered Value";
        }).thenAccept(System.out::println);
    • 3.1.6 等待多个任务完成

      • CompletableFuture.allOf(CompletableFuture<?>... cfs): 返回一个新的 CompletableFuture,当所有给定的 CompletableFuture 都完成时(正常或异常),它才完成。结果本身为 null。常用于等待所有任务结束。

      • CompletableFuture.anyOf(CompletableFuture<?>... cfs): 返回一个新的 CompletableFuture,当任意一个给定的 CompletableFuture 完成时(正常或异常),它就完成,结果与第一个完成的相同。

      • 示例 (allOf):

        java 复制代码
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task1");
        CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task2");
        CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> "Task3");
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2, task3);
        allTasks.thenRun(() -> {
            // 所有任务都已完成,可以安全获取结果
            try {
                System.out.println(task1.get() + ", " + task2.get() + ", " + task3.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    • 3.1.7 执行器控制

      • 所有 *Async 方法(如 supplyAsync, thenApplyAsync, thenComposeAsync)都有重载版本,允许传入一个 Executor 来指定任务在哪个线程池中执行。这提供了对线程使用的精细控制。
    • 3.1.8 超时控制 (Java 9+)

      • orTimeout(long timeout, TimeUnit unit): 给当前 CompletableFuture 设置一个超时。如果在超时时间内未完成,则异常完成 (TimeoutException)。

      • completeOnTimeout(T value, long timeout, TimeUnit unit): 如果在超时时间内未完成,则使用给定值 value 正常完成。

      • 示例 (orTimeout):

        java 复制代码
        CompletableFuture<String> slowTask = CompletableFuture.supplyAsync(() -> {
            sleep(2000); // 耗时 2 秒
            return "Done";
        });
        CompletableFuture<String> withTimeout = slowTask.orTimeout(1, TimeUnit.SECONDS); // 1秒超时
        withTimeout.exceptionally(ex -> {
            if (ex instanceof TimeoutException) {
                return "Timed Out";
            }
            return "Other Error";
        }).thenAccept(System.out::println); // 输出 "Timed Out"
    • 3.1.9 CompletableFuture 最佳实践

      • 优先使用链式操作 (thenApply, thenCompose, thenCombine) 而非阻塞的 get()
      • 明确处理异常 (exceptionally, handle)。
      • 合理使用 *Async 方法控制线程池,避免所有任务都在公共池中运行。
      • 注意组合操作(如 allOf)后获取各个任务结果的方式。
      • 使用超时控制防止任务长时间挂起。
      • 保持异步任务短小,避免在任务中执行长时间阻塞操作。
  • 3.2 Fork/Join 框架 (Java 7+)
    • 3.2.1 分治思想与工作窃取
      • 分治: 将一个大任务递归地分解成若干个小任务(子问题),直到任务足够小可以直接求解。然后将子任务的结果合并得到最终结果。
      • 工作窃取 (Work-Stealing): 每个工作线程维护一个双端队列 (Deque)。线程从自己队列的头部获取任务执行。当某个线程的队列为空时,它会尝试从其他线程队列的尾部"窃取"一个任务来执行。这有助于平衡线程负载。
    • 3.2.2 RecursiveTaskRecursiveAction
      • RecursiveTask<V>: 用于有返回值的任务。需要实现 compute() 方法,在其中进行任务拆分和结果合并。
      • RecursiveAction: 用于无返回值的任务。同样实现 compute() 方法。
    • 3.2.3 ForkJoinPool
      • 专门为执行 ForkJoinTask (RecursiveTask, RecursiveAction 的父类) 设计的线程池。
      • 通常使用 ForkJoinPool.commonPool() 获取公共池。
    • 3.2.4 适用场景与限制
      • 适用: 可递归分解的、计算密集型的任务(如排序、矩阵运算、大规模数据处理)。任务之间应尽可能独立。

      • 不适用: I/O 密集型任务(线程会阻塞),任务间依赖复杂,小任务(拆分和合并开销可能得不偿失)。

      • 示例 (使用 RecursiveTask 计算数组和):

        java 复制代码
        import java.util.concurrent.RecursiveTask;
        import java.util.concurrent.ForkJoinPool;
        public class ForkJoinSumCalculator extends RecursiveTask<Long> {
            private final long[] numbers;
            private final int start;
            private final int end;
            private static final long THRESHOLD = 10_000; // 阈值,小于此值直接计算
            public ForkJoinSumCalculator(long[] numbers) {
                this(numbers, 0, numbers.length);
            }
            private ForkJoinSumCalculator(long[] numbers, int start, int end) {
                this.numbers = numbers;
                this.start = start;
                this.end = end;
            }
            @Override
            protected Long compute() {
                int length = end - start;
                if (length <= THRESHOLD) {
                    return computeSequentially(); // 小于阈值,顺序计算
                }
                // 拆分任务
                int mid = start + length / 2;
                ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(numbers, start, mid);
                leftTask.fork(); // 异步执行左半部分
                ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(numbers, mid, end);
                Long rightResult = rightTask.compute(); // 同步执行右半部分 (也可以 fork,然后 join)
                Long leftResult = leftTask.join(); // 获取左半部分结果
                return leftResult + rightResult; // 合并结果
            }
            private long computeSequentially() {
                long sum = 0;
                for (int i = start; i < end; i++) {
                    sum += numbers[i];
                }
                return sum;
            }
            public static void main(String[] args) {
                long[] numbers = new long[100_000];
                for (int i = 0; i < numbers.length; i++) {
                    numbers[i] = i;
                }
                ForkJoinPool pool = ForkJoinPool.commonPool();
                Long result = pool.invoke(new ForkJoinSumCalculator(numbers));
                System.out.println("Sum: " + result); // 应输出 4999950000L
            }
        }

4. 响应式编程与 Reactive Streams

  • 4.1 响应式编程概念 (Reactive Programming)

    • 4.1.1 观察者模式、迭代器模式与数据流
      • 核心: 围绕异步数据流进行编程。数据流(如事件、消息、变量变化)可以被创建、组合、过滤、转换、订阅。
      • 观察者模式: 发布者 (Publisher / Observable) 产生数据流,订阅者 (Subscriber / Observer) 消费数据流。订阅者向发布者注册,发布者在数据可用时通知订阅者。
      • 迭代器模式 (拉取): 消费者主动从生产者拉取数据。响应式通常是推送模式:生产者主动将数据推送给消费者。
      • 优势: 声明式、可组合性、背压支持、非阻塞。
    • 4.1.2 背压 (Backpressure)
      • 问题: 当生产者产生数据的速度远快于消费者处理数据的速度时,消费者可能会被压垮(内存溢出、资源耗尽)。
      • 解决方案: 背压机制允许消费者向生产者反馈其处理能力。生产者根据消费者的需求调整数据生产速率。这是响应式编程区别于传统回调或 CompletableFuture 的关键特性之一。
  • 4.2 Reactive Streams 规范

    • Java 平台的一个标准规范 (JEP 266),定义了处理异步数据流(带背压)的接口。主要位于 java.util.concurrent.Flow 包 (Java 9+)。
    • 核心接口:
      • Flow.Publisher<T>: 数据流的发布者,可以向订阅者发布多个数据项 (onNext)、错误 (onError) 或完成信号 (onComplete)。
      • Flow.Subscriber<T>: 数据流的订阅者。接收来自发布者的通知。
      • Flow.Subscription: 代表 PublisherSubscriber 之间的一次订阅。Subscriber 通过它向 Publisher 请求数据 (request(long n)) 或取消订阅 (cancel())。
      • Flow.Processor<T,R>: 既是 Subscriber 也是 Publisher,用于转换数据流。
  • 4.3 Project Reactor 实现

    • 一个基于 Reactive Streams 规范的、非阻塞的响应式编程库。广泛应用于 Spring WebFlux。

    • 4.3.1 核心类型:MonoFlux

      • Flux<T>: 代表一个发出 0 到 N 个元素的异步序列。可以发出元素、完成信号或错误信号。适用于多个结果的流。
      • Mono<T>: 代表一个发出 0 或 1 个元素的异步序列。适用于单个结果(或空、错误)的场景。
    • 4.3.2 创建数据流

      • Flux 创建:

        java 复制代码
        Flux.just("A", "B", "C") // 固定元素
            Flux.fromIterable(Arrays.asList(1, 2, 3)) // 从集合
            Flux.fromStream(Stream.of("X", "Y", "Z")) // 从Stream
            Flux.range(1, 5) // 数字范围
            Flux.empty() // 空流
            Flux.error(new RuntimeException("Error")) // 错误流
            Flux.interval(Duration.ofSeconds(1)) // 定时发出序列 (0, 1, 2...)
            Flux.create(sink -> { ... }) // 自定义创建 (可多线程push)
            Flux.defer(() -> Flux.just(Instant.now())) // 延迟创建 (每次订阅时创建)
      • Mono 创建:

        java 复制代码
        Mono.just("Single")
            Mono.justOrEmpty(Optional.of("Maybe")) // 处理Optional
            Mono.empty()
            Mono.error(new Exception("Mono Error"))
            Mono.fromCallable(() -> "From Callable")
            Mono.fromFuture(CompletableFuture.supplyAsync(() -> "From CF"))
            Mono.fromRunnable(() -> System.out.println("Done")) // 无结果
    • 4.3.3 操作符

      • 提供了丰富的操作符对数据流进行转换、过滤、组合等操作。操作符本身是非阻塞的。

      • 常见操作符:

        • map(Function<T, R>): 将元素转换为另一种类型。
        • flatMap(Function<T, Publisher<R>>): 将每个元素转换为一个新的 Publisher (如 FluxMono),并将这些 Publisher 发出的元素合并到主序列中。用于异步转换。
        • filter(Predicate<T>): 过滤元素。
        • zip(Publisher<?>... sources, Function<? super Object[], R>): 将多个流的最新元素组合成一个元组或指定类型。等待所有流发出一个元素。
        • reduce(BiFunction<T, T, T>) / reduce(T initial, BiFunction<T, T, T>): 将流元素累积计算为一个结果。
        • collect(Collector): 将流元素收集到一个容器 (如 List, Map)。
        • mergeWith(Publisher): 合并两个流,元素按实际到达时间交错。
        • concatWith(Publisher): 连接两个流,先消费第一个流的所有元素,再消费第二个流。
        • buffer(int maxSize): 将流元素收集到缓冲区 (List),当缓冲区满或流完成时发出缓冲区。
        • window(int maxSize): 将流分割成多个窗口 (Flux<Flux<T>>),每个窗口包含最多 maxSize 个元素。
        • delayElements(Duration): 延迟发出每个元素。
        • onErrorResume(Function<Throwable, Publisher<T>>): 遇到错误时,切换到由函数提供的备用 Publisher
        • onErrorReturn(T): 遇到错误时,发出一个静态值然后正常结束。
        • retry() / retry(long): 在错误后重新订阅源 Publisher (重试)。
      • 示例:

        java 复制代码
        Flux.range(1, 10)
            .filter(n -> n % 2 == 0) // 过滤偶数
            .map(n -> n * 2) // 乘以2
            .flatMap(n -> (n > 10) ? Mono.just(n) : Flux.just(n, n)) // >10 发1次,<=10 发2次
            .subscribe(
                data -> System.out.println("Data: " + data), // onNext
                error -> System.err.println("Error: " + error), // onError
                () -> System.out.println("Done!") // onComplete
            );
        // 输出示例:
        // Data: 4
        // Data: 4
        // Data: 8
        // Data: 8
        // Data: 12
        // Data: 16
        // Data: 20
        // Done!
    • 4.3.4 调度器 (Schedulers) 与线程控制

      • 响应式操作默认在订阅发生的线程中执行。使用 publishOnsubscribeOn 可以控制执行上下文。

      • Schedulers: 提供预定义的线程池策略:

        • Schedulers.immediate(): 当前线程。
        • Schedulers.single(): 单一线程(可重用)。
        • Schedulers.parallel(): 固定大小的并行线程池 (CPU核心数)。
        • Schedulers.elastic() (已弃用) / Schedulers.boundedElastic(): 按需创建线程池,适合阻塞操作。
        • Schedulers.fromExecutor(Executor): 适配已有的 Executor
      • publishOn(Scheduler): 影响其之后的操作符和订阅者。切换后续操作的执行线程。

      • subscribeOn(Scheduler): 影响整个链路的订阅过程 (从源头开始)。指定源 Publisher 在哪个调度器上启动数据生成。通常用于阻塞的源头 (如数据库查询)。

      • 示例:

        java 复制代码
        Flux.range(1, 3)
            .map(i -> {
                System.out.println("Map1 on " + Thread.currentThread().getName()); // 可能主线程
                return i * 10;
            })
            .publishOn(Schedulers.parallel()) // 切换到并行线程池
            .map(i -> {
                System.out.println("Map2 on " + Thread.currentThread().getName()); // parallel-*
                return i + 1;
            })
            .subscribeOn(Schedulers.boundedElastic()) // 源头在弹性线程池启动 (如果源头阻塞)
            .subscribe();
    • 4.3.5 背压处理策略

      • 当订阅者通过 Subscription.request(n) 请求数据时,Publisher 才发送数据。

      • 订阅者可以控制请求的数据量,防止被压垮。

      • BaseSubscriber 类提供了自定义背压请求逻辑的便利方式。

      • 示例 (使用 BaseSubscriber):

        java 复制代码
        Flux.range(1, 100)
            .subscribe(new BaseSubscriber<Integer>() {
                @Override
                protected void hookOnSubscribe(Subscription subscription) {
                    request(1); // 初始请求1个元素
                }
                @Override
                protected void hookOnNext(Integer value) {
                    System.out.println("Received: " + value);
                    if (value % 10 == 0) {
                        request(10); // 每收到10的倍数,请求10个
                    } else {
                        request(1); // 否则请求1个
                    }
                }
            });
    • 4.3.6 测试 (StepVerifier)

      • Reactor 提供了 StepVerifier 工具来测试响应式序列。

      • 示例:

        java 复制代码
        import reactor.test.StepVerifier;
        Flux<String> flux = Flux.just("A", "B", "C").delayElements(Duration.ofMillis(100));
        StepVerifier.create(flux)
                .expectNext("A") // 期望下一个元素
                .expectNext("B")
                .expectNext("C")
                .expectComplete() // 期望完成
                .verify(Duration.ofSeconds(1)); // 验证超时
  • 4.4 其他响应式库简介

    • RxJava: 另一个流行的 ReactiveX 规范实现。API 与 Reactor 有相似之处,但历史更久,社区庞大。
    • Akka Streams: 基于 Actor 模型的响应式流库,提供更高级别的抽象和强大的流处理能力。常用于 Akka 生态系统。

5. 异步 IO 与 NIO

  • 5.1 Java NIO 基础

    • NIO (New I/O) 提供了非阻塞 I/O 操作的核心组件。
    • Channel: 类似于流,但支持异步读写。常见类型:FileChannel, SocketChannel, ServerSocketChannel, AsynchronousFileChannel, AsynchronousSocketChannel
    • Buffer: 数据容器。读写数据都通过 Buffer。有 ByteBuffer, CharBuffer, IntBuffer 等。
    • Selector: 允许单线程监控多个 Channel 的 I/O 事件(如连接就绪、读就绪、写就绪)。这是实现高并发网络服务器的关键。
    • (注:Selector 通常用于基于事件循环的网络编程模型,这里主要介绍更面向任务的异步 IO API)
  • 5.2 使用 AsynchronousFileChannel 进行异步文件操作

    • 示例 (异步读取文件):

      java 复制代码
      import java.nio.ByteBuffer;
      import java.nio.channels.AsynchronousFileChannel;
      import java.nio.file.Path;
      import java.nio.file.StandardOpenOption;
      import java.util.concurrent.CompletableFuture;
      public class AsyncFileRead {
          public static void main(String[] args) throws Exception {
              Path path = Path.of("test.txt");
              // 打开异步文件通道
              AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
              ByteBuffer buffer = ByteBuffer.allocate(1024);
              // 创建 CompletableFuture 来包装异步IO操作
              CompletableFuture<Integer> readFuture = new CompletableFuture<>();
              // 开始异步读取
              channel.read(buffer, 0, null, new CompletionHandler<Integer, Void>() {
                  @Override
                  public void completed(Integer bytesRead, Void attachment) {
                      readFuture.complete(bytesRead); // 读取完成,通知 future
                  }
                  @Override
                  public void failed(Throwable exc, Void attachment) {
                      readFuture.completeExceptionally(exc); // 读取失败,通知 future
                  }
              });
              // 非阻塞等待读取完成
              readFuture.thenAccept(bytesRead -> {
                  System.out.println("Read " + bytesRead + " bytes");
                  buffer.flip(); // 准备读取buffer
                  byte[] data = new byte[buffer.remaining()];
                  buffer.get(data);
                  System.out.println(new String(data));
                  try {
                      channel.close();
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              });
              System.out.println("Main thread continues...");
              Thread.sleep(2000); // 等待异步操作
          }
      }
  • 5.3 使用 AsynchronousSocketChannel 进行异步网络通信

    • 流程通常涉及异步连接、异步读写。使用 CompletionHandler 处理操作结果。

    • 示例 (简化客户端异步连接和读取):

      java 复制代码
      import java.net.InetSocketAddress;
      import java.nio.ByteBuffer;
      import java.nio.channels.AsynchronousSocketChannel;
      import java.nio.channels.CompletionHandler;
      public class AsyncSocketClient {
          public static void main(String[] args) throws Exception {
              AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
              // 异步连接
              client.connect(new InetSocketAddress("localhost", 8080), null, new CompletionHandler<Void, Void>() {
                  @Override
                  public void completed(Void result, Void attachment) {
                      System.out.println("Connected!");
                      ByteBuffer buffer = ByteBuffer.allocate(1024);
                      // 异步读取
                      client.read(buffer, null, new CompletionHandler<Integer, Void>() {
                          @Override
                          public void completed(Integer bytesRead, Void attachment) {
                              buffer.flip();
                              byte[] data = new byte[buffer.remaining()];
                              buffer.get(data);
                              System.out.println("Received: " + new String(data));
                              try {
                                  client.close();
                              } catch (Exception e) {
                                  e.printStackTrace();
                              }
                          }
                          @Override
                          public void failed(Throwable exc, Void attachment) {
                              exc.printStackTrace();
                          }
                      });
                  }
                  @Override
                  public void failed(Throwable exc, Void attachment) {
                      exc.printStackTrace();
                  }
              });
              System.out.println("Main thread continues...");
              Thread.sleep(5000); // 等待异步操作
          }
      }
  • 5.4 CompletionHandler 接口

    • 用于处理异步 I/O 操作结果的回调接口。
    • 定义了两个方法:
      • completed(V result, A attachment): 操作成功完成时调用。result 是操作结果(如读取的字节数),attachment 是发起操作时传入的附加对象。
      • failed(Throwable exc, A attachment): 操作失败时调用。exc 是异常信息。

6. 异步编程中的并发控制

  • 异步操作常涉及多线程访问共享资源,需要同步机制来保证正确性。

  • 6.1 原子类 (java.util.concurrent.atomic)

    • 提供对单个变量进行原子操作(不可中断)的类。避免使用锁。

    • AtomicInteger, AtomicLong, AtomicBoolean: 原子更新基本类型。

    • AtomicReference<V>: 原子更新对象引用。

    • LongAdder, DoubleAdder: 高并发下性能更好的累加器。

    • 示例 (AtomicInteger):

      java 复制代码
      import java.util.concurrent.atomic.AtomicInteger;
      public class AtomicExample {
          private static AtomicInteger counter = new AtomicInteger(0);
          public static void main(String[] args) throws InterruptedException {
              Runnable task = () -> {
                  for (int i = 0; i < 1000; i++) {
                      counter.incrementAndGet(); // 原子增加
                  }
              };
              Thread t1 = new Thread(task);
              Thread t2 = new Thread(task);
              t1.start();
              t2.start();
              t1.join();
              t2.join();
              System.out.println(counter.get()); // 总是 2000
          }
      }
  • 6.2 锁

    • ReentrantLock: 可重入互斥锁。提供比 synchronized 更灵活的锁定操作(如尝试获取锁、可中断获取锁、公平锁)。

    • StampedLock (Java 8+): 提供乐观读锁、悲观读锁和写锁。在某些读多写少的场景性能更好。

    • ReadWriteLock / ReentrantReadWriteLock: 分离读锁和写锁,允许多个读线程并发访问,但写线程独占。提高读多写少场景的性能。

    • 示例 (ReentrantLock):

      java 复制代码
      import java.util.concurrent.locks.ReentrantLock;
      public class LockExample {
          private final ReentrantLock lock = new ReentrantLock();
          private int count = 0;
          public void increment() {
              lock.lock(); // 获取锁
              try {
                  count++;
              } finally {
                  lock.unlock(); // 释放锁 (确保在 finally 中)
              }
          }
      }
  • 6.3 并发容器 (java.util.concurrent)

    • ConcurrentHashMap: 线程安全的 HashMap。高并发下性能优于 Collections.synchronizedMap
    • CopyOnWriteArrayList: 线程安全的 List。修改操作(增删)时复制整个底层数组。适用于读多写少且数据量不大的场景。
    • BlockingQueue: 阻塞队列接口。支持生产者-消费者模式。常用实现:ArrayBlockingQueue (有界), LinkedBlockingQueue (可选有界), SynchronousQueue (无容量), PriorityBlockingQueue (优先级)。
  • 6.4 避免共享可变状态

    • 最佳策略: 尽可能设计无状态的服务或使用 ThreadLocal 存储线程特定状态。避免多个线程同时修改共享数据。
    • 如果必须共享,则使用不可变对象 (final 字段) 或线程安全的容器/同步机制。

7. 异步编程最佳实践与性能调优

  • 7.1 选择合适的工具
    • 简单异步任务、结果依赖链: CompletableFuture
    • 复杂数据流处理、背压需求: 响应式编程 (Reactor, RxJava)。
    • 可递归分解的计算密集型任务: ForkJoinPool
    • 大量 I/O 密集型连接 (如网络服务器): NIO + Selector (事件循环) 或响应式 Netty/Vert.x。
    • 直接文件/网络异步 IO: NIO2 (Asynchronous*Channel).
  • 7.2 线程池配置原则
    • CPU 密集型: 线程数 ≈ CPU 核心数。过多线程导致频繁上下文切换。
    • I/O 密集型: 线程数可远大于 CPU 核心数。参考公式:线程数 = CPU核心数 \\times 目标CPU利用率 \\times (1 + 等待时间 / 计算时间)。实践中需通过压力测试调整。
    • 混合型: 拆分任务到不同线程池,或使用动态大小的线程池 (如 Schedulers.boundedElastic)。
    • 队列选择: 根据任务特性选择有界/无界队列。有界队列需设置合理的拒绝策略。
  • 7.3 资源管理与泄漏预防
    • 使用 try-with-resources 语句自动关闭实现了 AutoCloseable 接口的资源 (Connection, Channel, ExecutorService.shutdown() 后等待终止)。
    • 确保所有异步任务路径最终都能释放资源(即使在异常情况下)。
    • 监控资源使用情况 (连接数、线程数、内存)。
  • 7.4 异常处理策略
    • 在异步操作的各个层级(任务内部、CompletableFuture 链、响应式操作符)都要妥善处理异常。
    • 使用 CompletableFutureexceptionally/handle,响应式的 onError* 操作符。
    • 记录详细的异常日志,包括上下文信息。
    • 定义全局的异常处理回退机制。
  • 7.5 日志记录与调试技巧
    • 在关键点添加日志(任务开始/结束、重要状态变更、异常捕获)。
    • 记录线程名、关联ID (如请求ID) 以便追踪。
    • 使用调试器的异步堆栈跟踪功能(如果支持)。
    • 对于响应式流,可以使用 log() 操作符记录事件。
  • 7.6 性能监控与分析工具
    • JMX (Java Management Extensions): 监控线程池状态(活动线程数、队列大小等)。
    • Profiler (如 VisualVM, JProfiler, async-profiler): 分析CPU、内存、线程、锁竞争情况,找出性能瓶颈。
    • APM (Application Performance Monitoring) 工具: 分布式追踪、监控微服务性能。
  • 7.7 避免常见陷阱
    • 死锁: 多个任务相互等待对方释放资源。避免嵌套锁定或使用定时锁 (tryLock)。分析锁定顺序。
    • 活锁: 任务不断重试失败的操作(如冲突处理)。引入随机退避。
    • 线程饥饿: 某些线程长期得不到执行。确保公平性(公平锁、合理线程优先级)或使用足够大的线程池。
    • 回调地狱: 使用 CompletableFuture 的链式操作或响应式编程的声明式风格替代深层嵌套回调。
    • 忽略异常: 确保所有异步路径的异常都被记录或处理。
    • 资源泄漏: 严格遵循资源管理最佳实践。

8. 综合实战案例

  • 8.1 案例一:异步文件处理与上传

    • 场景: 服务器需要异步读取大文件,进行内容处理(如解析、过滤、统计),然后将处理结果异步上传到云存储。

    • 技术点: CompletableFuture, AsynchronousFileChannel, 线程池, 错误处理, 资源管理。

    • 简化代码示例:

      java 复制代码
      import java.nio.ByteBuffer;
      import java.nio.channels.AsynchronousFileChannel;
      import java.nio.file.Path;
      import java.nio.file.StandardOpenOption;
      import java.util.concurrent.CompletableFuture;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      public class AsyncFileProcessor {
          private static final ExecutorService fileIoExecutor = Executors.newFixedThreadPool(4);
          private static final ExecutorService processingExecutor = Executors.newWorkStealingPool();
          private static final ExecutorService uploadExecutor = Executors.newFixedThreadPool(2);
          public static CompletableFuture<Void> processAndUploadFile(Path filePath) {
              // 1. 异步读取文件内容
              CompletableFuture<String> contentFuture = readFileAsync(filePath, fileIoExecutor);
              // 2. 异步处理内容 (在另一个线程池)
              CompletableFuture<String> processedFuture = contentFuture.thenApplyAsync(content -> {
                  // 模拟耗时处理 (解析、过滤等)
                  return processContent(content);
              }, processingExecutor);
              // 3. 异步上传处理结果
              CompletableFuture<Void> uploadFuture = processedFuture.thenAcceptAsync(processedContent -> {
                  uploadToCloud(processedContent); // 模拟上传
              }, uploadExecutor);
              // 4. 返回最终的 Future (代表整个流程完成)
              return uploadFuture.exceptionally(ex -> {
                  System.err.println("Error processing/uploading file: " + ex.getMessage());
                  return null;
              });
          }
          private static CompletableFuture<String> readFileAsync(Path path, Executor executor) {
              return CompletableFuture.supplyAsync(() -> {
                  try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(path, StandardOpenOption.READ)) {
                      ByteBuffer buffer = ByteBuffer.allocate((int) channel.size());
                      CompletableFuture<Integer> readFuture = new CompletableFuture<>();
                      channel.read(buffer, 0, null, new CompletionHandler<Integer, Void>() {
                          @Override
                          public void completed(Integer bytesRead, Void attachment) {
                              readFuture.complete(bytesRead);
                          }
                          @Override
                          public void failed(Throwable exc, Void attachment) {
                              readFuture.completeExceptionally(exc);
                          }
                      });
                      return new String(buffer.array()); // 简化,实际需处理部分读取
                  } catch (Exception e) {
                      throw new RuntimeException(e);
                  }
              }, executor);
          }
          private static String processContent(String content) {
              // 模拟处理
              return content.toUpperCase(); // 简单示例
          }
          private static void uploadToCloud(String content) {
              // 模拟上传
              System.out.println("Uploading content: " + content.substring(0, Math.min(20, content.length())) + "...");
          }
          public static void main(String[] args) {
              Path file = Path.of("sample.txt"); // 假设存在
              processAndUploadFile(file).join();
              // 关闭线程池 (实际应用中应在适当时候关闭)
              fileIoExecutor.shutdown();
              processingExecutor.shutdown();
              uploadExecutor.shutdown();
          }
      }
  • 8.2 案例二:异步微服务调用聚合

    • 场景: 需要调用多个下游微服务获取数据,然后聚合这些结果返回给客户端。

    • 技术点: CompletableFuture 组合 (allOf, thenCombine), 超时控制, 错误处理, 服务降级/熔断 (简化演示)。

    • 简化代码示例:

      java 复制代码
      import java.util.Arrays;
      import java.util.List;
      import java.util.Map;
      import java.util.concurrent.*;
      import java.util.stream.Collectors;
      public class AsyncServiceAggregator {
          // 模拟调用用户服务
          public static CompletableFuture<String> callUserService(String userId) {
              return CompletableFuture.supplyAsync(() -> {
                  sleep(200 + ThreadLocalRandom.current().nextInt(300)); // 随机耗时 200-500ms
                  return "UserInfo for " + userId;
              }).orTimeout(400, TimeUnit.MILLISECONDS) // 设置400ms超时
               .exceptionally(ex -> "Fallback User Info"); // 超时或错误时返回降级数据
          }
          // 模拟调用订单服务
          public static CompletableFuture<String> callOrderService(String userId) {
              return CompletableFuture.supplyAsync(() -> {
                  sleep(150 + ThreadLocalRandom.current().nextInt(300)); // 随机耗时 150-450ms
                  return "OrderHistory for " + userId;
              }).orTimeout(400, TimeUnit.MILLISECONDS)
               .exceptionally(ex -> "Fallback Order History");
          }
          // 模拟调用推荐服务
          public static CompletableFuture<String> callRecommendationService(String userId) {
              return CompletableFuture.supplyAsync(() -> {
                  sleep(300 + ThreadLocalRandom.current().nextInt(400)); // 随机耗时 300-700ms
                  return "Recommendations for " + userId;
              }).orTimeout(500, TimeUnit.MILLISECONDS) // 推荐服务允许稍长超时
               .exceptionally(ex -> "Fallback Recommendations");
          }
          // 聚合多个服务结果
          public static CompletableFuture<Map<String, String>> aggregateServices(String userId) {
              CompletableFuture<String> userFuture = callUserService(userId);
              CompletableFuture<String> orderFuture = callOrderService(userId);
              CompletableFuture<String> recFuture = callRecommendationService(userId);
              // 等待所有服务调用完成 (无论成功或降级)
              return CompletableFuture.allOf(userFuture, orderFuture, recFuture)
                      .thenApply(v -> Map.of(
                              "user", userFuture.join(), // join() 不会抛异常,因为 exceptionally 处理了
                              "orders", orderFuture.join(),
                              "recommendations", recFuture.join()
                      ));
          }
          public static void main(String[] args) throws Exception {
              String userId = "user123";
              Map<String, String> result = aggregateServices(userId).get(); // 阻塞获取结果
              System.out.println("Aggregated Result:");
              result.forEach((key, value) -> System.out.println(key + ": " + value));
          }
          static void sleep(long millis) {
              try {
                  Thread.sleep(millis);
              } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
              }
          }
      }
  • 8.3 案例三:响应式 Web 应用 (使用 Spring WebFlux)

    • 场景: 构建一个非阻塞的 RESTful API,使用响应式方式访问数据库并返回数据流。

    • 技术点: Spring WebFlux, Project Reactor, 响应式数据库驱动 (如 R2DBC), 响应式流处理。

    • 简化代码示例 (需要 Spring Boot + WebFlux + R2DBC 依赖):

      java 复制代码
      // 1. 定义实体类
      public class User {
          private Long id;
          private String name;
          private String email;
          // Getters, Setters, Constructor
      }
      // 2. 定义响应式 Repository 接口 (使用 Spring Data R2DBC)
      import org.springframework.data.repository.reactive.ReactiveCrudRepository;
      public interface UserRepository extends ReactiveCrudRepository<User, Long> {
          Flux<User> findByName(String name);
      }
      // 3. 定义响应式 Service
      import org.springframework.stereotype.Service;
      import reactor.core.publisher.Flux;
      import reactor.core.publisher.Mono;
      @Service
      public class UserService {
          private final UserRepository userRepository;
          public UserService(UserRepository userRepository) {
              this.userRepository = userRepository;
          }
          public Flux<User> getAllUsers() {
              return userRepository.findAll();
          }
          public Mono<User> getUserById(Long id) {
              return userRepository.findById(id);
          }
          public Flux<User> getUsersByName(String name) {
              return userRepository.findByName(name);
          }
          public Mono<User> createUser(User user) {
              return userRepository.save(user);
          }
      }
      // 4. 定义响应式 Controller (使用 WebFlux)
      import org.springframework.web.bind.annotation.*;
      import reactor.core.publisher.Flux;
      import reactor.core.publisher.Mono;
      @RestController
      @RequestMapping("/users")
      public class UserController {
          private final UserService userService;
          public UserController(UserService userService) {
              this.userService = userService;
          }
          @GetMapping
          public Flux<User> getAllUsers() {
              return userService.getAllUsers();
          }
          @GetMapping("/{id}")
          public Mono<User> getUserById(@PathVariable Long id) {
              return userService.getUserById(id);
          }
          @GetMapping("/byName")
          public Flux<User> getUsersByName(@RequestParam String name) {
              return userService.getUsersByName(name);
          }
          @PostMapping
          public Mono<User> createUser(@RequestBody User user) {
              return userService.createUser(user);
          }
      }
    • 说明:

      • 整个链路(从 HTTP 请求处理到数据库访问)都是非阻塞和响应式的。
      • Controller 方法返回 MonoFlux 类型,Spring WebFlux 会自动处理订阅和响应。
      • 数据库操作通过 R2DBC 返回 Publisher (Mono/Flux)。
      • 可以轻松在 Service 或 Controller 中添加响应式操作符进行流处理。

这份指南涵盖了 Java 异步编程的核心概念、工具库、最佳实践和实战案例。希望它能帮助你有效地编写高性能、高响应性的 Java 应用程序。

相关推荐
shehuiyuelaiyuehao2 小时前
11String类型知识点
java·开发语言
毕设源码-赖学姐2 小时前
【开题答辩全过程】以 基于Java的图书馆座位预约管理系统设计为例,包含答辩的问题和答案
java·开发语言
qq_423233902 小时前
跨语言调用C++接口
开发语言·c++·算法
zhougl9962 小时前
Java Object.clone() 浅拷贝与深拷贝全解析
java·开发语言
飘若随风2 小时前
JS学习系列-01-什么是JS
开发语言·javascript·学习
余瑜鱼鱼鱼2 小时前
线程池总结
java·开发语言
定偶2 小时前
网络编程总结
开发语言·网络·数据结构·网络编程
w_t_y_y2 小时前
工具Cursor(三)MCP(3)常用的三方MCP Tools
java
claem2 小时前
Mac端 Python脚本创建与理解
开发语言·python·macos