这是一份非常详细、实用、通俗易懂、权威且全面的 Java 异步编程指南。内容涵盖从基础概念到高级应用,包含大量可直接运行的代码示例和完整的系统案例。
Java 异步编程全面指南
目录
-
理解异步编程
- 1.1 同步 vs 异步
- 1.2 阻塞 vs 非阻塞
- 1.3 为什么需要异步编程?
- 1.4 异步编程的核心挑战
-
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 最佳实践与常见陷阱
-
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
RecursiveTask与RecursiveAction - 3.2.3
ForkJoinPool - 3.2.4 适用场景与限制
- 3.1 CompletableFuture (Java 8+)
-
响应式编程与 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 核心类型:
Mono与Flux - 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.3.1 核心类型:
- 4.4 其他响应式库简介 (RxJava, Akka Streams)
- 4.1 响应式编程概念 (Reactive Programming)
-
异步 IO 与 NIO
- 5.1 Java NIO 基础 (
Channel,Buffer,Selector) - 5.2 使用
AsynchronousFileChannel进行异步文件操作 - 5.3 使用
AsynchronousSocketChannel进行异步网络通信 - 5.4
CompletionHandler接口
- 5.1 Java NIO 基础 (
-
异步编程中的并发控制
- 6.1 原子类 (
AtomicInteger,AtomicLong,AtomicReference,LongAdder) - 6.2 锁 (
ReentrantLock,StampedLock,ReadWriteLock) - 6.3 并发容器 (
ConcurrentHashMap,CopyOnWriteArrayList,BlockingQueue) - 6.4 避免共享可变状态
- 6.1 原子类 (
-
异步编程最佳实践与性能调优
- 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.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()方法)。 -
示例:
javapublic 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):
javaimport 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): 提供更细粒度的控制。javaint 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 的增强
CompletableFuture是Future的增强版,实现了Future和CompletionStage接口。- 支持异步任务的链式组合 、非阻塞回调 、异常处理 、合并多个任务结果等。
- 解决了
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的输入。 -
示例:
javaimport 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):javaCompletableFuture<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): 无论阶段正常完成还是异常完成,都会调用fn。fn接收结果或异常,并返回一个新结果。类似于finally里获取结果或异常。 -
whenComplete(BiConsumer<? super T, ? super Throwable> action): 无论阶段正常完成还是异常完成,都会调用action消费结果或异常。不改变结果值。用于日志记录等副作用操作。 -
示例 (
exceptionally):javaCompletableFuture.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):javaCompletableFuture<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):javaCompletableFuture<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
RecursiveTask与RecursiveActionRecursiveTask<V>: 用于有返回值的任务。需要实现compute()方法,在其中进行任务拆分和结果合并。RecursiveAction: 用于无返回值的任务。同样实现compute()方法。
- 3.2.3
ForkJoinPool- 专门为执行
ForkJoinTask(RecursiveTask,RecursiveAction的父类) 设计的线程池。 - 通常使用
ForkJoinPool.commonPool()获取公共池。
- 专门为执行
- 3.2.4 适用场景与限制
-
适用: 可递归分解的、计算密集型的任务(如排序、矩阵运算、大规模数据处理)。任务之间应尽可能独立。
-
不适用: I/O 密集型任务(线程会阻塞),任务间依赖复杂,小任务(拆分和合并开销可能得不偿失)。
-
示例 (使用
RecursiveTask计算数组和):javaimport 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 } }
-
- 3.2.1 分治思想与工作窃取
4. 响应式编程与 Reactive Streams
-
4.1 响应式编程概念 (Reactive Programming)
- 4.1.1 观察者模式、迭代器模式与数据流
- 核心: 围绕异步数据流进行编程。数据流(如事件、消息、变量变化)可以被创建、组合、过滤、转换、订阅。
- 观察者模式: 发布者 (
Publisher/Observable) 产生数据流,订阅者 (Subscriber/Observer) 消费数据流。订阅者向发布者注册,发布者在数据可用时通知订阅者。 - 迭代器模式 (拉取): 消费者主动从生产者拉取数据。响应式通常是推送模式:生产者主动将数据推送给消费者。
- 优势: 声明式、可组合性、背压支持、非阻塞。
- 4.1.2 背压 (Backpressure)
- 问题: 当生产者产生数据的速度远快于消费者处理数据的速度时,消费者可能会被压垮(内存溢出、资源耗尽)。
- 解决方案: 背压机制允许消费者向生产者反馈其处理能力。生产者根据消费者的需求调整数据生产速率。这是响应式编程区别于传统回调或
CompletableFuture的关键特性之一。
- 4.1.1 观察者模式、迭代器模式与数据流
-
4.2 Reactive Streams 规范
- Java 平台的一个标准规范 (JEP 266),定义了处理异步数据流(带背压)的接口。主要位于
java.util.concurrent.Flow包 (Java 9+)。 - 核心接口:
Flow.Publisher<T>: 数据流的发布者,可以向订阅者发布多个数据项 (onNext)、错误 (onError) 或完成信号 (onComplete)。Flow.Subscriber<T>: 数据流的订阅者。接收来自发布者的通知。Flow.Subscription: 代表Publisher和Subscriber之间的一次订阅。Subscriber通过它向Publisher请求数据 (request(long n)) 或取消订阅 (cancel())。Flow.Processor<T,R>: 既是Subscriber也是Publisher,用于转换数据流。
- Java 平台的一个标准规范 (JEP 266),定义了处理异步数据流(带背压)的接口。主要位于
-
4.3 Project Reactor 实现
-
一个基于 Reactive Streams 规范的、非阻塞的响应式编程库。广泛应用于 Spring WebFlux。
-
4.3.1 核心类型:
Mono与FluxFlux<T>: 代表一个发出 0 到 N 个元素的异步序列。可以发出元素、完成信号或错误信号。适用于多个结果的流。Mono<T>: 代表一个发出 0 或 1 个元素的异步序列。适用于单个结果(或空、错误)的场景。
-
4.3.2 创建数据流
-
Flux创建:javaFlux.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创建:javaMono.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(如Flux或Mono),并将这些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(重试)。
-
示例:
javaFlux.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) 与线程控制
-
响应式操作默认在订阅发生的线程中执行。使用
publishOn和subscribeOn可以控制执行上下文。 -
Schedulers: 提供预定义的线程池策略:Schedulers.immediate(): 当前线程。Schedulers.single(): 单一线程(可重用)。Schedulers.parallel(): 固定大小的并行线程池 (CPU核心数)。Schedulers.elastic()(已弃用) /Schedulers.boundedElastic(): 按需创建线程池,适合阻塞操作。Schedulers.fromExecutor(Executor): 适配已有的Executor。
-
publishOn(Scheduler): 影响其之后的操作符和订阅者。切换后续操作的执行线程。 -
subscribeOn(Scheduler): 影响整个链路的订阅过程 (从源头开始)。指定源Publisher在哪个调度器上启动数据生成。通常用于阻塞的源头 (如数据库查询)。 -
示例:
javaFlux.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):javaFlux.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工具来测试响应式序列。 -
示例:
javaimport 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进行异步文件操作-
示例 (异步读取文件):
javaimport 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处理操作结果。 -
示例 (简化客户端异步连接和读取):
javaimport 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):javaimport 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):javaimport 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链、响应式操作符)都要妥善处理异常。 - 使用
CompletableFuture的exceptionally/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, 线程池, 错误处理, 资源管理。 -
简化代码示例:
javaimport 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), 超时控制, 错误处理, 服务降级/熔断 (简化演示)。 -
简化代码示例:
javaimport 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 方法返回
Mono或Flux类型,Spring WebFlux 会自动处理订阅和响应。 - 数据库操作通过 R2DBC 返回
Publisher(Mono/Flux)。 - 可以轻松在 Service 或 Controller 中添加响应式操作符进行流处理。
-
这份指南涵盖了 Java 异步编程的核心概念、工具库、最佳实践和实战案例。希望它能帮助你有效地编写高性能、高响应性的 Java 应用程序。