面试题答案
一键面试结合点
- 异步处理基础:两者都旨在解决异步编程问题,允许在不阻塞主线程的情况下执行任务,提高系统的响应性和资源利用率。例如,在处理网络请求或数据库查询时,都能异步执行,避免线程长时间等待。
- 链式调用与组合操作:CompletableFuture 通过 thenApply、thenCompose 等方法实现链式调用和操作组合;RxJava 通过 map、flatMap 等操作符实现类似功能。比如,在对异步任务结果进行转换处理时,两者都能方便地进行链式操作。
主要差异
- 编程模型:
- CompletableFuture 基于 Future 模式,主要以回调的方式处理异步结果,是命令式编程风格。例如:
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + ", World")
.thenAccept(System.out::println);
- RxJava 基于观察者模式,采用声明式编程风格。它更注重数据流的处理和订阅关系。例如:
Observable.just("Hello")
.map(s -> s + ", World")
.subscribe(System.out::println);
- 背压处理:
- CompletableFuture 没有内置的背压处理机制,适用于任务数量可预测、数据量相对较小的场景。
- RxJava 有完善的背压处理机制,当上游产生数据的速度快于下游处理速度时,能有效处理数据流动,适合数据量较大且流速不可控的场景。
- 操作符丰富度:
- CompletableFuture 提供的操作符相对有限,主要围绕任务的完成、转换和组合。
- RxJava 拥有大量丰富的操作符,如过滤、合并、窗口化等,能更灵活地处理复杂的异步数据流。
场景选择
- 选择 CompletableFuture 的场景:
- 简单异步任务组合:当项目中异步任务逻辑相对简单,只需要基本的任务组合和结果处理时。例如,在一个电商系统中,查询商品库存和查询商品价格这两个异步任务,之后简单合并结果返回给前端。
CompletableFuture<Integer> stockFuture = CompletableFuture.supplyAsync(() -> getStock());
CompletableFuture<Double> priceFuture = CompletableFuture.supplyAsync(() -> getPrice());
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(stockFuture, priceFuture);
combinedFuture.thenRun(() -> {
int stock = stockFuture.join();
double price = priceFuture.join();
System.out.println("Stock: " + stock + ", Price: " + price);
}).join();
- **与现有 Java 生态集成**:项目基于 Java 标准库开发,对响应式编程框架引入成本较高,希望尽量复用现有 Java 技术栈时。
2. 选择响应式编程(RxJava)的场景: - 复杂数据流处理:在实时数据处理系统,如股票交易系统中,需要对大量实时数据进行过滤、聚合、窗口化等复杂操作时。例如,统计每分钟内股票价格的平均值:
Observable.interval(1, TimeUnit.SECONDS)
.map(i -> getStockPrice())
.window(60)
.flatMap(window -> window.reduce(0.0, (sum, price) -> sum + price)
.map(avg -> avg / 60))
.subscribe(System.out::println);
- **背压敏感场景**:在处理高流量的网络数据接收,如下载大量文件或接收高并发的实时消息时,需要有效处理背压问题。