这是一份非常详细、实用、通俗易懂、权威且全面的 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 适用场景与限制
响应式编程与 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.4 其他响应式库简介 (RxJava, Akka Streams)
异步 IO 与 NIO
- 5.1 Java NIO 基础 (Channel, Buffer, Selector)
- 5.2 使用 AsynchronousFileChannel 进行异步文件操作
- 5.3 使用 AsynchronousSocketChannel 进行异步网络通信
- 5.4 CompletionHandler 接口
异步编程中的并发控制
- 6.1 原子类 (AtomicInteger, AtomicLong, AtomicReference, LongAdder)
- 6.2 锁 (ReentrantLock, StampedLock, ReadWriteLock)
- 6.3 并发容器 (ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue)
- 6.4 避免共享可变状态
异步编程最佳实践与性能调优
- 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() 方法)。
- 示例: 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): 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): // 固定大小线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
// 单线程线程池 (保证任务顺序执行)
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 缓存线程池 (根据需要创建新线程,空闲线程存活 60 秒)
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 计划任务线程池 (可延迟或周期性执行任务)
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2); - 直接创建 (ThreadPoolExecutor): 提供更细粒度的控制。 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);
- 工厂方法 (Executors): // 固定大小线程池
- 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;线程池过大导致上下文切换开销过高。
- 死锁: 任务间相互等待对方持有的资源。
- 性能问题: 线程池配置不当(大小、队列、拒绝策略)。
- 最佳实践:
- 2.3.1 线程池原理与优势
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的输入。
- 示例: 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): 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): 无论阶段正常完成还是异常完成,都会调用 fn。fn 接收结果或异常,并返回一个新结果。类似于 finally 里获取结果或异常。
- whenComplete(BiConsumer<? super T, ? super Throwable> action): 无论阶段正常完成还是异常完成,都会调用 action 消费结果或异常。不改变结果值。用于日志记录等副作用操作。
- 示例 (exceptionally): 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): 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): 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.1.1 核心概念:Future 的增强
- 3.2 Fork/Join 框架 (Java 7+)
- 3.2.1 分治思想与工作窃取
- 分治: 将一个大任务递归地分解成若干个小任务(子问题),直到任务足够小可以直接求解。然后将子任务的结果合并得到最终结果。
- 工作窃取 (Work-Stealing): 每个工作线程维护一个双端队列 (Deque)。线程从自己队列的头部获取任务执行。当某个线程的队列为空时,它会尝试从其他线程队列的尾部“窃取”一个任务来执行。这有助于平衡线程负载。
- 3.2.2 RecursiveTask 与 RecursiveAction
- RecursiveTask<V>: 用于有返回值的任务。需要实现 compute() 方法,在其中进行任务拆分和结果合并。
- RecursiveAction: 用于无返回值的任务。同样实现 compute() 方法。
- 3.2.3 ForkJoinPool
- 专门为执行 ForkJoinTask (RecursiveTask, RecursiveAction 的父类) 设计的线程池。
- 通常使用 ForkJoinPool.commonPool() 获取公共池。
- 3.2.4 适用场景与限制
- 适用: 可递归分解的、计算密集型的任务(如排序、矩阵运算、大规模数据处理)。任务之间应尽可能独立。
- 不适用: I/O 密集型任务(线程会阻塞),任务间依赖复杂,小任务(拆分和合并开销可能得不偿失)。
- 示例 (使用 RecursiveTask 计算数组和): 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
}
}
- 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,用于转换数据流。
- 4.3 Project Reactor 实现
- 一个基于 Reactive Streams 规范的、非阻塞的响应式编程库。广泛应用于 Spring WebFlux。
- 4.3.1 核心类型:Mono 与 Flux
- Flux<T>: 代表一个发出 0 到 N 个元素的异步序列。可以发出元素、完成信号或错误信号。适用于多个结果的流。
- Mono<T>: 代表一个发出 0 或 1 个元素的异步序列。适用于单个结果(或空、错误)的场景。
- 4.3.2 创建数据流
- Flux 创建: 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 创建: 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")) // 无结果
- Flux 创建: Flux.just("A", "B", "C") // 固定元素
- 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 (重试)。
- 示例: 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) 与线程控制
- 响应式操作默认在订阅发生的线程中执行。使用 publishOn 和 subscribeOn 可以控制执行上下文。
- Schedulers: 提供预定义的线程池策略:
- Schedulers.immediate(): 当前线程。
- Schedulers.single(): 单一线程(可重用)。
- Schedulers.parallel(): 固定大小的并行线程池 (CPU核心数)。
- Schedulers.elastic() (已弃用) / Schedulers.boundedElastic(): 按需创建线程池,适合阻塞操作。
- Schedulers.fromExecutor(Executor): 适配已有的 Executor。
- publishOn(Scheduler): 影响其之后的操作符和订阅者。切换后续操作的执行线程。
- subscribeOn(Scheduler): 影响整个链路的订阅过程(从源头开始)。指定源 Publisher 在哪个调度器上启动数据生成。通常用于阻塞的源头 (如数据库查询)。
- 示例: 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): 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 工具来测试响应式序列。
- 示例: 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 进行异步文件操作
- 示例 (异步读取文件): 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); // 等待异步操作
}
}
- 示例 (异步读取文件): import java.nio.ByteBuffer;
- 5.3 使用 AsynchronousSocketChannel 进行异步网络通信
- 流程通常涉及异步连接、异步读写。使用 CompletionHandler 处理操作结果。
- 示例 (简化客户端异步连接和读取): 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): 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): 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 链、响应式操作符)都要妥善处理异常。
- 使用 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, 线程池, 错误处理, 资源管理。
- 简化代码示例: 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), 超时控制, 错误处理, 服务降级/熔断 (简化演示)。
- 简化代码示例: 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 依赖): // 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 应用程序。

