在 completionstage 中使用函数式接口,核心是调用 accepteitherasync 方法并传入一个实现了 biconsumer 接口的 lambda 表达式。该方法的参数类型固定为 completionstage extends t> 和 biconsumer super t, ? super t>,这意味着你需要提供一个接收两个相同类型参数的消费型函数,当当前阶段或指定阶段完成时,它会处理其中一个的结果。

如何定义 BiConsumer 来处理两个阶段的竞争结果
要使用 acceptEitherAsync,必须提供 BiConsumer 类型的回调。这个接口接受两个参数,分别代表两个竞争阶段中获胜的那个结果(另一个参数通常为 null 或未使用,具体取决于实现细节,但在标准 API 中它总是接收两个值)。在实际编码中,我们通常只关注实际完成的那个值。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<string> future1 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(100); } catch (InterruptedException e) {}
return "Result from Future 1";
});
CompletableFuture<string> future2 = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(50); } catch (InterruptedException e) {}
return "Result from Future 2";
});
// 使用 acceptEitherAsync
future1.acceptEitherAsync(future2, (res1, res2) -> {
// 这里 res1 和 res2 中有一个是有意义的,另一个可能是 null 或者未初始化
// 实际上,acceptEitherAsync 的 BiConsumer 接收的是两个 CompletionStage 的结果
// 但根据 JDK 文档,它接收的是两个 T 类型的值
System.out.println("Winner: " + (res1 != null ? res1 : res2));
});
// 等待主线程结束以便观察输出
Thread.sleep(200);
}
}</string></string>
异步执行与线程池配置的影响
acceptEitherAsync允许指定自定义的 Executor 来运行回调逻辑。如果不指定,默认使用 ForkJoinPool.commonPool()。通过传入特定的线程池,可以控制并发行为和资源隔离。

import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws Exception {
ExecutorService customPool = Executors.newFixedThreadPool(2);
CompletableFuture<integer> f1 = CompletableFuture.supplyAsync(() -> {
System.out.println("F1 running on: " + Thread.currentThread().getName());
return 10;
});
CompletableFuture<integer> f2 = CompletableFuture.supplyAsync(() -> {
System.out.println("F2 running on: " + Thread.currentThread().getName());
return 20;
});
// 指定自定义线程池
f1.acceptEitherAsync(f2, (r1, r2) -> {
System.out.println("Callback running on: " + Thread.currentThread().getName());
System.out.println("Result: " + (r1 != null ? r1 : r2));
}, customPool);
Thread.sleep(1000);
customPool.shutdown();
}
}</integer></integer>
当两个阶段同时完成时如何处理
如果两个 CompletionStage 几乎同时完成,acceptEitherAsync 的行为是确定性的:它会选择其中一个作为“获胜者”并触发回调。具体的选择取决于底层实现的调度顺序,通常先完成的那个会被优先处理,但如果时间片重叠,可能任意一个。开发者不应依赖哪个特定阶段获胜,而应确保回调逻辑对任一输入都是安全的。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











