返回一个CompletableFuture而不暴露执行程序线程

时间:2019-06-09 17:03:53

标签: java concurrency completable-future

我在库中公开一个方法,该方法返回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

我发现的一个解决方案是引入另一个执行器,并在返回CompletableFuture之前向该执行器添加一个no-op thenApplyAsync

    CompletableFuture<String> asyncOp(String param) {
        return CompletableFuture.supplyAsync(() -> {
            log("In asyncOp");
            return "Hello " + param;
        }, preciousExecService).thenApplyAsync(s -> s, secondExecService);
    }

这行得通,但并不觉得超级优雅-有更好的方法吗?

2 个答案:

答案 0 :(得分:0)

您可以更改方法签名以返回Future而不是CompletableFuture

Future<String> asyncOp(String param) {
    return CompletableFuture.supplyAsync(() -> {
        log("In asyncOp");
        return "Hello " + param;
    }, preciousExecService);
}

那样,run()方法将引发编译错误:

void run() {
    log("run");
    asyncOp("world").thenAccept(this::syncOp);
}

调用者仍然可以将返回的Future强制转换回CompletableFuture,但这将是对您的API的滥用,并且不会偶然发生。

答案 1 :(得分:0)

没有任何功能可以使您的完成与从属操作的执行分开。当链接相关动作的线程已经完成注册并且您的执行者的线程完成了将来时,如果没有其他执行者被授予,哪个线程应该执行该相关动作?

您将方法与其他执行程序链接在一起的方法似乎是最好的。但是,请务必注意,如果完成异常,则传播异常时不会评估传递给thenApply的函数。如果调用者链接了whenCompletehandleexceptionally之类的操作,则此异常传播可能再次导致线程暴露。

另一方面,您无需指定辅助执行程序,因为您可以使用不带执行程序参数的 async 方法来获取默认(公共Fork / Join)池。 / p>

因此,到目前为止,.whenCompleteAsync((x,y) -> {})链接是解决您问题的最佳解决方案。