Java中使用同步回調(diào)和異步回調(diào)的示例詳解
(一)同步回調(diào)
同步回調(diào)函數(shù)將始終在執(zhí)行某些操作后立即執(zhí)行。這意味著它將與執(zhí)行該操作的函數(shù)同步。
在觀察者設(shè)計(jì)模式中可以找到回調(diào)函數(shù)的示例。在需要單擊按鈕以啟動(dòng)某些操作的應(yīng)用界面中,我們可以將回調(diào)函數(shù)作為該按鈕單擊的監(jiān)聽器傳遞。監(jiān)聽器函數(shù)等待按鈕被單擊,然后執(zhí)行監(jiān)聽器回調(diào)。
(1)匿名內(nèi)部類回調(diào)
每當(dāng)我們將帶有方法實(shí)現(xiàn)的接口傳遞給 Java 中的另一個(gè)方法時(shí),我們都在使用回調(diào)函數(shù)的概念。在下面的代碼中,我們將通過 Consumer 功能接口和一個(gè)匿名內(nèi)部類(沒有名稱的實(shí)現(xiàn))來實(shí)現(xiàn) accept() 方法。
實(shí)現(xiàn) accept() 方法后,我們將執(zhí)行 performAction 方法中的操作;然后我們將從 Consumer 接口執(zhí)行 accept() 方法:
import java.util.function.Consumer; /** * 同步場(chǎng)景下匿名內(nèi)部類的方式實(shí)現(xiàn)回調(diào) * * @author zhangyu * @date 2023/4/16 */ public class AnonymousClassCallback { public static void main(String[] args) { performAction(new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }); } public static void performAction(Consumer<String> consumer) { System.out.println("執(zhí)行特定的業(yè)務(wù)邏輯"); consumer.accept("回調(diào)代碼被執(zhí)行"); } }
在這段代碼中,我們將 Consumer 接口傳遞給 performAction() 方法,然后在操作完成后調(diào)用 accept() 方法。
(2)Lambda 回調(diào)
觀察上面的代碼可能還會(huì)注意到使用匿名內(nèi)部類非常冗長(zhǎng)。改用 lambda 會(huì)好得多。
/** * 同步場(chǎng)景下匿名內(nèi)部類的方式實(shí)現(xiàn)回調(diào)(Lambda寫法) * * @author zhangyu * @date 2023/4/16 */ public class LambdaCallback { public static void main(String[] args) { performAction(() -> System.out.println("回調(diào)代碼被執(zhí)行")); } public static void performAction(Runnable runnable) { System.out.println("執(zhí)行特定的業(yè)務(wù)邏輯"); runnable.run(); } }
輸出再次表明正在執(zhí)行操作并執(zhí)行回調(diào)。
(二)異步回調(diào)
通常,我們希望使用異步回調(diào)方法,這意味著將在操作之后調(diào)用但與其他線程異步調(diào)用的方法。當(dāng)不需要在其他線程之后立即調(diào)用回調(diào)方法時(shí),這可能有助于提高性能。
(1)簡(jiǎn)單的線程回調(diào)
在下面的代碼中,首先我們將從 Runnable 功能接口實(shí)現(xiàn) run() 方法。然后,我們將創(chuàng)建一個(gè) Thread 并使用我們剛剛在 Thread 中實(shí)現(xiàn)的 run() 方法。最后,我們啟動(dòng)線程異步執(zhí)行:
/ /** * 異步回調(diào)實(shí)例 * * @author zhangyu * @date 2023/4/16 */ public class AsynchronousCallback { public static void main(String[] args) { Runnable runnable = () -> System.out.println("回調(diào)代碼被執(zhí)行"); AsynchronousCallback asynchronousCallback = new AsynchronousCallback(); asynchronousCallback.performAsynchronousAction(runnable); } public void performAsynchronousAction(Runnable runnable) { new Thread(() -> { System.out.println("執(zhí)行異步操作代碼"); runnable.run(); }).start(); } }
在上面的代碼中,首先我們?yōu)?Runnable 中的 run() 方法創(chuàng)建了一個(gè)實(shí)現(xiàn)。然后,我們調(diào)用了 performAsynchronousAction() 方法,傳遞帶有 run() 方法實(shí)現(xiàn)的可運(yùn)行功能接口。 在 performAsynchronousAction() 中,我們傳遞 runnable 接口并使用 lambda 在 Thread 中實(shí)現(xiàn)另一個(gè) Runnable 接口。
(2)異步并行回調(diào)
除了在異步操作中調(diào)用回調(diào)函數(shù)之外,我們還可以在調(diào)用另一個(gè)函數(shù)的同時(shí)調(diào)用回調(diào)函數(shù)。這意味著我們可以啟動(dòng)兩個(gè)線程并并行調(diào)用這些方法。 代碼將與前面的示例類似,但請(qǐng)注意,我們將啟動(dòng)一個(gè)新線程并在這個(gè)新線程中調(diào)用回調(diào)函數(shù),而不是直接調(diào)用回調(diào)函數(shù):
/** * 異步并行回調(diào) * * @author zhangyu * @date 2023/4/16 */ public class AsynchronousParallelCallback { public void performAsynchronousAction(Runnable runnable) { new Thread(() -> { System.out.println("執(zhí)行異步操作代碼"); // 創(chuàng)建一個(gè)新的線程執(zhí)行回調(diào) new Thread(runnable).start(); }).start(); } public static void main(String[] args) { Runnable runnable = () -> System.out.println("回調(diào)代碼被執(zhí)行"); AsynchronousParallelCallback callback = new AsynchronousParallelCallback(); callback.performAsynchronousAction(runnable); } }
當(dāng)我們不需要在 performAsynchronousAction() 方法的操作之后立即執(zhí)行回調(diào)函數(shù)時(shí),異步并行回調(diào)很有用。 一個(gè)真實(shí)的例子是當(dāng)我們?cè)诰€購(gòu)買產(chǎn)品時(shí),我們不需要等到確認(rèn)付款、檢查庫(kù)存以及所有那些繁重的流程。在這種情況下,我們可以在后臺(tái)執(zhí)行回調(diào)調(diào)用的同時(shí)做其他事情。CompletableFuture 回調(diào)
(3)CompletableFuture 回調(diào)
另一種使用異步回調(diào)函數(shù)的方法是使用 CompletableFuture API。這個(gè)強(qiáng)大的 API 在 Java 8 中引入,有助于執(zhí)行和組合異步方法調(diào)用。它完成了我們?cè)谇懊娴氖纠兴龅囊磺校鐒?chuàng)建一個(gè)新線程然后啟動(dòng)和管理它。 在下面的代碼示例中,我們將創(chuàng)建一個(gè)新的 CompletableFuture,然后我們將調(diào)用傳遞字符串的 supplyAsync 方法。 接下來,我們將創(chuàng)建另一個(gè) CompletableFuture,然后應(yīng)用一個(gè)回調(diào)函數(shù)來執(zhí)行我們配置的第一個(gè)函數(shù):
import java.util.concurrent.CompletableFuture; /** * CompletableFuture callback * * @author zhangyu * @date 2023/4/16 */ public class CompletableFutureCallback { public static void main(String[] args) throws Exception { CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("執(zhí)行業(yè)務(wù)代碼"); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("執(zhí)行業(yè)務(wù)代碼結(jié)束"); return "ok"; }); CompletableFuture<String> execution = completableFuture .thenApply(s -> s + "回調(diào)被執(zhí)行"); System.out.println("main線程執(zhí)行代碼"); System.out.println(execution.get()); } }
注意在上面的代碼示例中,CompletableFuture開啟了一個(gè)新的線程,因此其對(duì)應(yīng)的代碼是在新的線程中執(zhí)行的,因?yàn)榇a中Thread.sleep(2000);所以main線程執(zhí)行代碼內(nèi)容先打印了出來。再者可以看到在CompletableFuture執(zhí)行代碼和后面的回調(diào)代碼是按照順序執(zhí)行的。
小結(jié)
【1】回調(diào)函數(shù)應(yīng)該在執(zhí)行另一個(gè)動(dòng)作時(shí)或與該動(dòng)作并行執(zhí)行。
【2】回調(diào)函數(shù)可以是同步的,這意味著它必須在其他操作之后立即執(zhí)行,沒有任何延遲。
【3】回調(diào)函數(shù)可以是異步的,這意味著它可以在后臺(tái)執(zhí)行,并且可能需要一些時(shí)間才能執(zhí)行。
【4】異步回調(diào)需要注意,是在等待當(dāng)前執(zhí)行代碼完畢后在執(zhí)行異步回調(diào)還是允許直接啟動(dòng)異步線程,允許業(yè)務(wù)代碼和回調(diào)業(yè)務(wù)沒有執(zhí)行順序要求。
【5】對(duì)于回調(diào)的代碼場(chǎng)景應(yīng)用可以結(jié)合觀察者設(shè)計(jì)模式深入了解。
完整代碼
https://github.com/zwzhangyu/ZyCodeHub.git
到此這篇關(guān)于Java中使用同步回調(diào)和異步回調(diào)的示例詳解的文章就介紹到這了,更多相關(guān)java同步回調(diào)和異步回調(diào)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot集成logback打印彩色日志的代碼實(shí)現(xiàn)
Logback是由log4j創(chuàng)始人設(shè)計(jì)的另一個(gè)開源日志組件,默認(rèn)情況下,Spring?Boot會(huì)用Logback來記錄日志,并用INFO級(jí)別輸出到控制臺(tái),本文給大家介紹了SpringBoot集成logback打印彩色日志,需要的朋友可以參考下2024-03-03Java實(shí)現(xiàn)LeetCode(組合總和)
這篇文章主要介紹了Java實(shí)現(xiàn)LeetCode(組合總數(shù)),本文通過使用java實(shí)現(xiàn)leetcode的組合總數(shù)題目和實(shí)現(xiàn)思路分析,需要的朋友可以參考下2021-06-06詳解Java的文件與目錄管理以及輸入輸出相關(guān)操作
這篇文章主要介紹了詳解Java的文件與目錄管理以及輸入輸出相關(guān)操作,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09Java如何通過線程解決生產(chǎn)者/消費(fèi)者問題
這篇文章主要介紹了Java如何通過線程解決生產(chǎn)者/消費(fèi)者問題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-10-10java字符轉(zhuǎn)碼的三種方法總結(jié)及實(shí)例
這篇文章主要介紹了 java字符轉(zhuǎn)碼的三種方法總結(jié)及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03springboot項(xiàng)目啟動(dòng)指定對(duì)應(yīng)環(huán)境的方法
這篇文章主要介紹了springboot項(xiàng)目啟動(dòng)指定對(duì)應(yīng)環(huán)境的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08