最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中CompletableFuture四種調(diào)用模式的實現(xiàn)

 更新時間:2025年08月08日 09:26:01   作者:樺說編程  
CompletableFuture有就地執(zhí)行、異步執(zhí)行等三種模式,就地執(zhí)行適合輕量級任務,但可能導致線程阻塞,異步執(zhí)行有上下文切換開銷,本文介紹了第四種調(diào)用模式,解決了以上問題

三種調(diào)用模式

CompletableFuture(以下簡稱CF)提供了三種調(diào)用模式,分別是就地執(zhí)行、異步使用默認執(zhí)行器執(zhí)行、異步指定執(zhí)行器執(zhí)行。

就地執(zhí)行指的是回調(diào)在當前線程中執(zhí)行,調(diào)用thenApply、thenCompose等方法時,如果當前 CF 已經(jīng)執(zhí)行完成,會立即執(zhí)行回調(diào),稱為當前執(zhí)行,執(zhí)行時刻為當前(now);如果未完成,則由完成 CF 的線程執(zhí)行。

如下分別是立即執(zhí)行和異步執(zhí)行的例子:

var cf = CompletableFuture.completed(1);
cf.thenApply(x -> x + 1)
  .thenRun(x -> System.out.println(x))
  .join();

以上代碼全程同步。

var cf = new CompletableFuture<Integer>();
cf.thenApply(x -> x + 1)
  .thenRun(x -> System.out.println(x));
new Thread(() -> cf.complete(1)).start();
Uninterruptible.sleep(1, TimeUnit.SECONDS);

thenApply、thenRun在調(diào)用時,cf未完成,無法立刻執(zhí)行,其執(zhí)行在完成cf的線程,也就是新創(chuàng)建的線程中。

異步執(zhí)行指的是回調(diào)任務的執(zhí)行必定在執(zhí)行器中執(zhí)行,默認執(zhí)行器為Java提供的commonPool線程池,當然也可以通過重寫defaultExecutor實現(xiàn)調(diào)用指定的線程池。

var cf = CompletableFuture.completed(1);
cf.thenApplyAsync(x -> x + 1)
  .thenRunAsync(x -> System.out.println(x))
  .join();
Uninterruptible.sleep(1, TimeUnit.SECONDS);

以上代碼中打印操作在公共線程池中執(zhí)行。

比較

就地執(zhí)行性能最好,可以完全避免線程上下文切換,適合執(zhí)行一些輕量級任務。缺點是使用不當時,會阻塞當前線程;可能會造成“線程泄露”,導致線程池中的線程沒有及時歸還。

異步執(zhí)行反之。

第 4 種調(diào)用模式

線程池中任務執(zhí)行有一條原則:盡最大努力交付。意思是如果任務提交時沒有拒絕,沒有拋出拒絕執(zhí)行等異常,通常來說通過了信號量、限流器、執(zhí)行時間、線程數(shù)等諸多限制,后續(xù)的執(zhí)行應該不作額外限制,且努力完成;而不是等執(zhí)行過程中再拋出類似拒絕服務等異常。反過來說,如果當前任務提交時,任務不能執(zhí)行,就應該拒絕執(zhí)行。這條簡單的原則可以避免考慮復雜的問題,比如反壓、取消機制等,也能夠應對大多數(shù)的業(yè)務場景。

對于非輕量級任務,例如 A -> B,表示任務A執(zhí)行完成后執(zhí)行任務B,常規(guī)的線程池實現(xiàn)有一個問題,B任務的提交不一定立即執(zhí)行,可能遇到排隊(進入阻塞隊列)甚至超時等情況,最終導致整個任務的滯后。此時如果能就地執(zhí)行最好。

如果選擇就地執(zhí)行策略,解決了以上問題,但是可能會導致CF已完成后執(zhí)行的當前線程阻塞。這時最好有執(zhí)行器執(zhí)行任務,而不是占用當前線程。

最近CFFU類庫提供LLCF#relayAsync0,完美解決了以上痛點。LL表示low level,對于其的正確使用要求開發(fā)人員對CompletableFuture有著充分的理解。relay的含義是接力,這里指的是

  • relay Async 接力異步
  • Async 詞尾,保證一定是異步(和CF命名表義 一樣)

異步時(不阻塞調(diào)用邏輯),用前個computation的線程接力執(zhí)行,不使用新線程,避免了上下文切換開銷。

例子

relayAsync0 簽名如下:

public static <T, F extends CompletionStage<?>> F relayAsync0(
        CompletionStage<? extends T> cfThis,
        Function<CompletableFuture<T>, F> relayComputations, Executor executor)

需要注意傳入的回調(diào)任務不是普通的Function,而是入?yún)F,出參 CompletionStage,也就是說我們需要傳入對CF的回調(diào)。比如:

cf -> cf.thenApply(...)
cf -> cf.thenCompose(...)
cf -> cf.thenRun(...)

該方法使用時和thenApplyAsync很像,只不過由實例方法調(diào)用改成了靜態(tài)方法調(diào)用,回調(diào)參數(shù)為對CF的回調(diào)。

以下代碼引用自CFFU作者 李鼎 | Jerry Lee,詳細說明四種調(diào)用模式的用法:

public class RelayAsyncDescriptionAndExample {
    static void executeComputationsOfNewStage(CompletableFuture<String> cf) {

        // ================================================================================
        // Default execution
        // ================================================================================

        cf.thenApply(s -> {
            // a simulating long-running computation...
            sleep(1000);
            // if input cf is COMPLETED when computations execute,
            // executes the long time computation SYNCHRONOUSLY (aka. in the caller thread);
            // this SYNCHRONIZED execution leads to BLOCKing sequential codes of caller... ??

            return s + s;
        });

        // ================================================================================
        // Asynchronous execution of CompletableFuture(default executor or custom executor)
        // ================================================================================

        cf.thenApplyAsync(s -> {
            // a simulating long-running computation...
            sleep(1000);
            // always executes via an executor(guarantees not to block sequential code of caller).
            // if input cf is INCOMPLETE when computations execute,
            // the execution via an executor leads to ONE MORE thread switching. ??

            return s + s;
        });

        // ================================================================================
        // How about the fourth way to arrange execution of a new stage's computations?
        // ================================================================================
        //
        // - if input cf is COMPLETED when computations execute, use "asynchronous execution" (via supplied Executor),
        //   won't block sequential code of caller ?
        // - otherwise, use "default execution", save one thread switching ?
        //
        // Let's call this way as "relay async".

        LLCF.relayAsync0(cf, f -> f.thenApply(s -> {
            // a simulating long-running computation...
            sleep(1000);
            // if input cf is COMPLETED, executes via supplied executor
            // if input cf is INCOMPLETE, use "default execution"

            return s + s;
        }), ForkJoinPool.commonPool());
    }
}

實現(xiàn)分析

public static <T, F extends CompletionStage<?>> F relayAsync0(
        CompletionStage<? extends T> cfThis,
        Function<CompletableFuture<T>, F> relayComputations, Executor executor) {
    final CompletableFuture<T> promise = new CompletableFuture<>();
    final F ret = relayComputations.apply(promise);

    final Thread callerThread = currentThread();
    final boolean[] returnedFromPeek0 = {false};

    LLCF.peek0(cfThis, (v, ex) -> {
        if (currentThread().equals(callerThread) && !returnedFromPeek0[0]) {
            // If the action is running in the caller thread(single same thread) and `peek0` invocation does not
            // return to caller(flag returnedFromPeek0 is false), the action is being executed synchronously.
            // To prevent blocking the caller's sequential code, use the supplied executor to complete the promise.
            executor.execute(() -> completeCf0(promise, v, ex));
        } else {
            // Otherwise, complete the promise directly, avoiding one thread switching.
            completeCf0(promise, v, ex);
        }
    }, "relayAsync0");

    returnedFromPeek0[0] = true;
    return ret;
}

說明:

  1. completeCf0方法可以將結(jié)果v或者異常ex設置到promise中
  2. peek0 近似等效于 whenComplete

分析:

  1. 可以通過引入新的CF,也就是 promise 實現(xiàn)線程傳遞,其他線程“完成”promise時,這個線程隱式傳到了promise中,可以理解成隱式上下文。任何一個CF都帶有一個隱式上下文。
  2. returnedFromPeek0 避免了異步調(diào)用但是恰好是同線程的問題,此時也應該實現(xiàn)relay語義,因為我們的目的是避免對當前線程的阻塞。returnedFromPeek0 天然線程安全,因為其訪問總是在一個確定的線程內(nèi)。
  3. else 代碼塊:就地執(zhí)行,避免線程切換。

總結(jié)

到此這篇關(guān)于Java中CompletableFuture四種調(diào)用模式的實現(xiàn)的文章就介紹到這了,更多相關(guān)Java CompletableFuture 調(diào)用模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springCloud gateWay 統(tǒng)一鑒權(quán)的實現(xiàn)代碼

    springCloud gateWay 統(tǒng)一鑒權(quán)的實現(xiàn)代碼

    這篇文章主要介紹了springCloud gateWay 統(tǒng)一鑒權(quán)的實現(xiàn)代碼,統(tǒng)一鑒權(quán)包括鑒權(quán)邏輯和代碼實現(xiàn),本文給大家介紹的非常詳細,需要的朋友可以參考下
    2022-02-02
  • RabbitMQ基礎概念之信道channel詳解

    RabbitMQ基礎概念之信道channel詳解

    這篇文章主要介紹了RabbitMQ基礎概念之信道channel詳解,信道是生產(chǎn)消費者與rabbit通信的渠道,生產(chǎn)者publish或者消費者消費一個隊列都是需要通過信道來通信的,需要的朋友可以參考下
    2023-08-08
  • Java中的Native方法

    Java中的Native方法

    這篇文章主要介紹了Java中的Native方法,在本文中,我們將看到java中本機native方法的介紹。我們將看到它的基本語法及其工作原理。將有java代碼示例展示native本機方法的使用,下面來看看文章的具體介紹
    2021-12-12
  • 關(guān)于Object中equals方法和hashCode方法判斷的分析

    關(guān)于Object中equals方法和hashCode方法判斷的分析

    今天小編就為大家分享一篇關(guān)于關(guān)于Object中equals方法和hashCode方法判斷的分析,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • Java日期時間類及計算詳解

    Java日期時間類及計算詳解

    這篇文章主要介紹了Java日期時間類及計算詳解,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下,希望對你的學習有所幫助
    2022-07-07
  • Java數(shù)據(jù)類型超完整指南

    Java數(shù)據(jù)類型超完整指南

    Java數(shù)據(jù)類型可以分為基本數(shù)據(jù)類型和引用數(shù)據(jù)類型兩大類,這篇文章主要介紹了Java數(shù)據(jù)類型的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2026-03-03
  • SpringBoot修改內(nèi)置tomcat版本的操作步驟

    SpringBoot修改內(nèi)置tomcat版本的操作步驟

    生產(chǎn)環(huán)境使用的外部部署Tomcat還是內(nèi)置Tomcat由于版本安全漏洞,往往需要升級到指定的安全版本,本文演示一下SpringBoot升級內(nèi)置的Tomcat版本,感興趣的小伙伴跟著小編一起來看看吧
    2024-07-07
  • Java如何在PDF中添加ToolTip工具提示

    Java如何在PDF中添加ToolTip工具提示

    大家好,本篇文章主要講的是Java如何在PDF中添加ToolTip工具提示,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • Mybatis many=@Many的傳值問題解決

    Mybatis many=@Many的傳值問題解決

    本文詳細介紹了在MyBatis通過主查詢將單個或多個值傳遞給子查詢的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-11-11
  • Java如何判斷一個字符串是否包含某個字符串

    Java如何判斷一個字符串是否包含某個字符串

    這篇文章主要給大家介紹了關(guān)于Java如何判斷一個字符串是否包含某個字符串的相關(guān)資料,在實際編程中,經(jīng)常需要判斷一個字符串中是否包含某個子串,需要的朋友可以參考下
    2023-07-07

最新評論

海伦市| 城口县| 晋宁县| 平南县| 佛坪县| 庆城县| 军事| 东乌| 洪湖市| 义乌市| 同心县| 任丘市| 聊城市| 滦平县| 普宁市| 北海市| 沂南县| 五台县| 合水县| 恩施市| 保定市| 开平市| 永州市| 弋阳县| 恭城| 海晏县| 石柱| 伊春市| 小金县| 历史| 桐柏县| 邯郸市| 内黄县| 化隆| 荔浦县| 裕民县| 锦州市| 疏附县| 阿荣旗| 临安市| 清新县|