【发布时间】:2019-10-24 06:42:28
【问题描述】:
我在库中公开了一个方法,该方法返回一个 CompletableFuture。该方法的计算发生在单线程执行器上,这是我的瓶颈,因此我不希望任何后续工作发生在同一个线程上。
如果我使用返回“supplyAsync”结果的简单方法,我会将我宝贵的线程暴露给调用者,他们可能正在添加同步操作(例如通过 thenAccept),这可能会在该线程上占用一些 CPU 时间。
复制如下:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CfPlayground {
private ExecutorService preciousExecService = Executors.newFixedThreadPool(1);
CfPlayground() {}
private static void log(String msg) {
System.out.println("[" + Thread.currentThread().getName() + "] " + msg);
}
CompletableFuture<String> asyncOp(String param) {
return CompletableFuture.supplyAsync(() -> {
log("In asyncOp");
return "Hello " + param;
}, preciousExecService);
}
void syncOp(String salutation) {
log("In syncOp: " + salutation);
}
void run() {
log("run");
asyncOp("world").thenAccept(this::syncOp);
}
public static void main(String[] args) throws InterruptedException {
CfPlayground compFuture = new CfPlayground();
compFuture.run();
Thread.sleep(500);
compFuture.preciousExecService.shutdown();
}
}
这确实打印:
[main] run
[pool-1-thread-1] In asyncOp
[pool-1-thread-1] In syncOp: Hello world
我发现的一个解决方案是引入另一个 Executor,并在返回 CompletableFuture 之前添加一个 no-op thenApplyAsync 与该 executor
CompletableFuture<String> asyncOp(String param) {
return CompletableFuture.supplyAsync(() -> {
log("In asyncOp");
return "Hello " + param;
}, preciousExecService).thenApplyAsync(s -> s, secondExecService);
}
这可行,但感觉不是特别优雅 - 有没有更好的方法来做到这一点?
【问题讨论】:
-
您的解决方案似乎是最好的解决方案。您可以传递ForkJoinPool.commonPool(),而不是创建第二个 ExecutorService,如果没有给出明确的 Executor,这是 CompletableFutures 默认使用的。 (我不完全清楚没有显式 Executor 参数的方法是否总是使用 commonPool,或者总是使用当前 CompletableFuture 的 Executor。)
-
感谢@VGR 的建议,它可以代替第二个执行者工作。为了完整起见,我的理解是,没有显式执行程序的异步回调将在 ForkJoinPool.commonPool() 上运行,如果 CompletableFuture 立即完成,非异步回调将在调用线程上运行,或者在完成请求的线程上运行(例如我的单线程执行器)否则。有更准确完整的解释here
标签: java concurrency completable-future