来自内部的{CompletableFuture注射

时间:2016-11-07 21:17:21

标签: java asynchronous concurrency java-8 future

是否可以从内部 注入 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内同步,我希望它以异步方式运行,当结果返回时直接进入下一阶段。我遇到的问题是条件只知道链中。

有人有这方面的想法吗?

修改

我会尝试澄清这个问题。我最初只had一段代码。现在我尝试优化代码,以便生成较少数量的线程。

关键是在注入点我想发布类似的内容(抱歉,用于Cassandra 代码片段的Datastax Java驱动程序):

ResultSetFuture rsFuture = session.executeAsync(query);

并将未来注入链中。这将使调用线程#34; free&#34;执行其他事情而不是坐着等待结果。

我不知道我是否可以比这更明确,但让我们按照这个例子。

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

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

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

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

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

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

  3. 在我看来,选项 2 优于选项 1 ,并且相同的推理可以应用于尺寸为&gt;的线程池。 1 或动态尺寸。

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

    希望这是有道理的。如果没有,请你解释一下我错在哪里?

2 个答案:

答案 0 :(得分:4)

不使用thenApplyAsync,而是使用thenComposethenComposeAsync,这会让函数返回CompletableFuture<Foo>而不是Foo。如果return true 满足some condition,则代替return CompletableFuture.completedFuture(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() { .... }

<android.support.v7.widget.Toolbar
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:fitsSystemWindows="true"
    android:minHeight="?attr/actionBarSize"
    app:theme="@style/MyTheme.ActionBarStyle"
    android:background="@android:color/transparent">

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="-10dp"
        android:gravity="center">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/explore_morning"
            android:contentDescription="@string/app_name" />
    </RelativeLayout>

</android.support.v7.widget.Toolbar>

答案 1 :(得分:0)

看起来你认为链接阶段之间的分裂工作(涉及“异步”)不知何故神奇地为你的程序逻辑增加了并发性改进。

当您链接阶段时,即使您使用其中一个“异步”方法,也会创建直接的顺序依赖关系,因为后续依赖阶段的执行在上一个完成之前没有开始。所以这种链接增加了昂贵的线程跳跃的机会,即不同的线程执行下一阶段,但没有提高并发性,因为最多只有一个线程处理你的一个阶段。实际上,同一个线程碰巧执行所有阶段的可能情况可能是执行速度最快。

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

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;
}

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