簡體   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