繁体   English   中英

从内部完成ComputableFuture注入

[英]CompletableFuture injection from the inside

是否可以从内部CompletableFuture链中注入 CompletableFuture

我正在使用这样的功能:

public CompletableFuture<Boolean> getFutureOfMyLongRunningTask() {
    CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        // ... Some processing here ...
        if (somecondition failed)
            return false; // Task failed!

        return true; // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // ... Some processing here ...

        if (!some condition satisfied) {
            // This is where I want the injection to happen. 
            // This stage should be suspended and a new stage should be injected between this point and the next stage.
        }

        return true; // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // ... Some processing here ...

        return true; // OK
    });

    // This is the result we have to wait for.
    return future;
}

在注入点if (!some condition satisfied) ,我想运行一个查询(例如),该查询需要5秒钟执行并检索最后阶段所需的一些数据。 我不想阻塞线程5秒钟,例如,使查询在if同步,我希望它异步运行,然后返回结果直接进入下一阶段。 我遇到的问题是该条件仅是已知的。

有人对此有想法吗?


编辑

我将尝试澄清这个问题。 我本来一条唯一的代码。 现在,我正在尝试优化代码,以便产生更少的线程。

关键是在注入点我想发出类似的消息(对不起, Cassandra代码片段的Datastax Java驱动程序 ):

ResultSetFuture rsFuture = session.executeAsync(query);

并注入未来进入连锁。 这将使调用线程“无需”执行其他操作,而不必坐下来等待结果。


我不知道是否可以比这更清楚,但是让我们跟随这个例子。

我在主线程中运行一个循环:

for (int i = 0; i < 1000; i++) {
    getFutureOfMyLongRunningTask(i);
}

该循环仅存在于主线程上,但是对函数的每次调用都会在线程池P中 排队一个新任务。 现在假设P是大小为1的固定线程池。 这意味着P中仅存在一个线程,并且只能处理1个任务。 但是,主循环将使所有1000个任务入队。 然后,主循环将需要等待所有任务完成。

现在假设1000个任务中的第一个任务需要执行一个长数据库查询。 现在,我们有两个选择:

  1. 该查询在处理线程内部 (属于线程池P同步执行。 这意味着我只是在if (!some condition satisfied)块内发出查询,然后等待结果。 这有效地阻止了任务处理,因为线程池P没有空闲线程。 唯一一个忙于 IO 阻塞

  2. 该查询是在处理线程内部 异步执行的(属于线程池P ),这意味着我在if (!some condition satisfied)块内发出查询,并立即返回一个我将要收听的未来(可能是DB驱动程序)将产生另一个线程并阻止线程等待结果)。 但是,属于P的线程现在可以自由处理至少另一个任务。

在我看来,选项2优于选项1 ,并且相同的推理可以应用于大小> 1或动态大小的线程池。

我想要的是保持线程池尽可能的空闲,以产生最少数量的线程,以避免浪费资源。

希望有道理。 如果没有,请您能解释我错了吗?

代替使用thenApplyAsync ,可以使用thenComposethenComposeAsync ,它们使函数返回CompletableFuture<Foo>而不是Foo 如果需要满足some condition 需要return CompletableFuture.completedFuture(true) ,而不是return true

public CompletableFuture<Boolean> getFutureOfMyLongRunningTask() {
    CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        // ... Some processing here ...
        if (somecondition failed)
            return false; // Task failed!

        return true; // OK
    }).thenComposeAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return CompletableFuture.completedFuture(false);

        // ... Some processing here ...

        if (!some condition satisfied) {
            return runSomeOtherQuery()
        }

        return CompletableFuture.completedFuture(true); // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // ... Some processing here ...

        return true; // OK
    });

    // This is the result we have to wait for.
    return future;
}


public CompletableFuture<Boolean> runSomeOtherQuery() {
    ....
}

似乎您正在考虑在链式阶段之间进行工作拆分(涉及“异步”)以某种方式神奇地为您的程序逻辑增加了并发性。

链接阶段时,即使使用“异步”方法之一,也将创建直接的顺序依赖关系,因为后续的依赖阶段的执行不会在前一个依赖阶段完成之前开始。 因此,这种链接增加了昂贵的线程跳跃的机会,即,另一个线程执行下一阶段,但又不提高并发性,因为最多会有一个线程处理您的一个阶段。 实际上,同一线程恰好执行所有阶段的可能性仍然是最快的。

有一种表达依赖关系的简单得多的自然方法。 只需在一段代码中一个接一个地编写动作。 您仍然可以安排该代码块进行异步执行。 因此,如果您的出发点是

public CompletableFuture<Boolean> getFutureOfMyLongRunningTask() {
    CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        // First stage processing here ...
        if (somecondition failed)
            return false; // Task failed!

        return true; // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // Second stage processing here ...

        if (!some condition satisfied) {
            // This is where I want the injection to happen. 
            // This stage should be suspended and a new stage should be
            // injected between this point and the next stage.
        }

        return true; // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // Third stage processing here ...

        return true; // OK
    });

    // This is the result we have to wait for.
    return future;
}

只需将其更改为

public CompletableFuture<Boolean> getFutureOfMyLongRunningTask() {
    CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        // First stage processing here ...
        if (somecondition failed)
            return false; // Task failed!
        // Second stage processing here ...

        if (!some condition satisfied) {
            // alternative "injected" stage processing
            if(injected stage failed)
                return false;
        }
        // Third stage processing here ...

        return true; // OK
    });

    // This is the result we have to wait for.
    return future;
}

更短更清晰。 您不必反复检查上一个阶段的成功。 您仍然具有相同的并发性,但是执行效率可能更高。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM