测试含CompletableFuture.allOf()的方法:allOf还是anyOf?
问题描述
我正在测试一个使用CompletableFuture.allOf()的方法,方法实现如下:
static CompletableFuture<byte[]> anySuccess(List<CompletableFuture<byte[]>> futures) { CompletableFuture<byte[]> delegateFuture = new CompletableFuture<>(); CompletableFuture.allOf(futures.stream().map(s -> s.thenApply(t -> { if (t == null) { // 这里把null视为查找失败 // 内部的虚拟异常用来给调用者传递返回null的信号 throw new RuntimeException(); } return t; }).thenAccept(delegateFuture::complete)).toArray(CompletableFuture<?>[]::new)) .exceptionally(ex -> { delegateFuture.completeExceptionally(ex); return null; }); return delegateFuture; }
输入的CompletableFuture集合会在不同时间完成,我的单元测试代码如下:
@Test void testAnySuccess() { final byte[] payload1 = "payload1".getBytes(); final byte[] payload2 = "payload2".getBytes(); List<CompletableFuture<byte[]>> futures = Arrays.asList( CompletableFuture.supplyAsync( () -> null ), CompletableFuture.supplyAsync( () -> { LockSupport.parkNanos(Duration.ofSeconds(5).toNanos()); System.out.println("Wait for 5 seconds"); return payload2; }), CompletableFuture.supplyAsync( () -> { LockSupport.parkNanos(Duration.ofMillis(100).toNanos()); System.out.println("Wait for 100ms"); return payload1; }) ); long start = System.nanoTime(); CompletableFuture<byte[]> res = anySuccess(futures); byte[] actualRes = res.join(); Assert.assertEquals(actualRes, payload1); }
我清楚CompletableFuture.allOf()是非阻塞的,它返回的CompletableFuture要等所有子Future完成后才会完成。我的核心需求是:返回第一个非null值,如果所有值都是null则抛出异常。现在有两个疑问:
- 这种场景下应该用
allOf还是anyOf? - 是否需要修改代码,去掉
exceptionally()改用thenAccept()?
解答
1. 优先选anyOf,而非allOf
你的需求是获取第一个成功返回非null的结果,allOf的特性是必须等待所有子Future完成才会结束,完全和需求相悖——用它的话你得等最慢的那个Future执行完,完全浪费了“取第一个有效结果”的优势。
anyOf才是匹配需求的选择:它会在任意一个子Future完成时就触发回调,正好能帮你实现“拿到第一个非null结果就返回”的逻辑。不过要注意,anyOf返回的是CompletableFuture<Object>,需要做类型转换,同时要处理其中可能的null或异常情况。
2. 代码修改建议
原代码存在两个核心问题:
- 依赖
allOf导致无法提前返回第一个有效结果,必须等全部Future完成 - 把null转为RuntimeException的逻辑没必要,直接判断结果是否为null更直观
下面是优化后的实现:
static CompletableFuture<byte[]> anySuccess(List<CompletableFuture<byte[]>> futures) { CompletableFuture<byte[]> resultFuture = new CompletableFuture<>(); // 给每个Future绑定回调:一旦返回非null结果,立刻完成resultFuture for (CompletableFuture<byte[]> future : futures) { future.thenAccept(result -> { if (result != null) { resultFuture.complete(result); } }).exceptionally(ex -> { // 单个Future的异常直接忽略,我们只关心有效结果 return null; }); } // 监听所有Future完成事件:如果此时resultFuture未完成,说明全是null,抛出异常 CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenRun(() -> { if (!resultFuture.isDone()) { resultFuture.completeExceptionally(new RuntimeException("All futures returned null")); } }); return resultFuture; }
这个实现的逻辑更清晰:
- 遍历每个Future,只要有一个返回非null值,立刻通过
resultFuture返回结果,实现“第一个有效结果快速返回” - 用
allOf兜底:当所有Future都完成但没有任何有效结果时,抛出异常满足需求 - 去掉了原代码中不必要的异常转换,直接通过结果判断逻辑更简洁
另外,原代码中的exceptionally可以调整:因为我们只关心成功的非null结果,单个Future的异常可以直接忽略,只有当所有Future都没返回有效结果时才抛出异常。
内容的提问来源于stack exchange,提问作者chrisrhyno2003
相关产品推荐
相关产品推荐

