0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

異步編程的幾種種實(shí)現(xiàn)方式(上)

jf_78858299 ? 來(lái)源:微觀技術(shù) ? 作者:Tom哥 ? 2023-02-15 16:15 ? 次閱讀

早期的系統(tǒng)是同步的,容易理解,我們來(lái)看個(gè)例子

同步編程

圖片

當(dāng)用戶創(chuàng)建一筆電商交易訂單時(shí),要經(jīng)歷的業(yè)務(wù)邏輯流程還是很長(zhǎng)的,每一步都要耗費(fèi)一定的時(shí)間,那么整體的RT就會(huì)比較長(zhǎng)。

于是,聰明的人們開始思考能不能將一些非核心業(yè)務(wù)從主流程中剝離出來(lái),于是有了異步編程雛形。

異步編程是讓程序并發(fā)運(yùn)行的一種手段。它允許多個(gè)事件同時(shí)發(fā)生,當(dāng)程序調(diào)用需要長(zhǎng)時(shí)間運(yùn)行的方法時(shí),它不會(huì)阻塞當(dāng)前的執(zhí)行流程,程序可以繼續(xù)運(yùn)行。

圖片

核心思路:采用多線程優(yōu)化性能,將串行操作變成并行操作。異步模式設(shè)計(jì)的程序可以顯著減少線程等待,從而在高吞吐量場(chǎng)景中,極大提升系統(tǒng)的整體性能,顯著降低時(shí)延。

接下來(lái),我們來(lái)講下異步有哪些編程實(shí)現(xiàn)方式

一、線程 Thread

直接繼承 Thread類 是創(chuàng)建異步線程最簡(jiǎn)單的方式。

首先,創(chuàng)建Thread子類,普通類或匿名內(nèi)部類方式;然后創(chuàng)建子類實(shí)例;最后通過(guò)start()方法啟動(dòng)線程。

public class AsyncThread extends Thread{
    @Override
    public void run() {
        System.out.println("當(dāng)前線程名稱:" + this.getName() + ", 執(zhí)行線程名稱:" + Thread.currentThread().getName() + "-hello");
    }
}
public static void main(String[] args) {

  // 模擬業(yè)務(wù)流程
  // .......
  
    // 創(chuàng)建異步線程 
    AsyncThread asyncThread = new AsyncThread();

    // 啟動(dòng)異步線程
    asyncThread.start();
}

當(dāng)然如果每次都創(chuàng)建一個(gè) Thread線程,頻繁的創(chuàng)建、銷毀,浪費(fèi)系統(tǒng)資源。我們可以采用線程池

@Bean(name = "executorService")
public ExecutorService downloadExecutorService() {
    return new ThreadPoolExecutor(20, 40, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2000),
            new ThreadFactoryBuilder().setNameFormat("defaultExecutorService-%d").build(),
            (r, executor) -> log.error("defaultExecutor pool is full! "));
}

將業(yè)務(wù)邏輯封裝到 RunnableCallable 中,交由 線程池 來(lái)執(zhí)行

圖片

二、Future

上述方式雖然達(dá)到了多線程并行處理,但有些業(yè)務(wù)不僅僅要執(zhí)行過(guò)程,還要獲取執(zhí)行結(jié)果。

Java 從1.5版本開始,提供了 CallableFuture,可以在任務(wù)執(zhí)行完畢之后得到任務(wù)執(zhí)行結(jié)果。

當(dāng)然也提供了其他功能,如:取消任務(wù)、查詢?nèi)蝿?wù)是否完成等

Future類位于java.util.concurrent包下,接口定義:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

方法描述:

  • cancel():取消任務(wù),如果取消任務(wù)成功返回true,如果取消任務(wù)失敗則返回false
  • isCancelled():表示任務(wù)是否被取消成功,如果在任務(wù)正常完成前被取消成功,則返回 true
  • isDone():表示任務(wù)是否已經(jīng)完成,如果完成,返回true
  • get():獲取執(zhí)行結(jié)果,這個(gè)方法會(huì)產(chǎn)生阻塞,會(huì)一直等到任務(wù)執(zhí)行完畢才返回
  • get(long timeout, TimeUnit unit):用來(lái)獲取執(zhí)行結(jié)果,如果在指定時(shí)間內(nèi),還沒獲取到結(jié)果,就直接返回null

代碼示例:

public class CallableAndFuture {

    public static ExecutorService executorService = new ThreadPoolExecutor(4, 40,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue(1024), new ThreadFactoryBuilder()
            .setNameFormat("demo-pool-%d").build(), new ThreadPoolExecutor.AbortPolicy());


    static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "異步處理,Callable 返回結(jié)果";
        }
    }

    public static void main(String[] args) {
        Future future = executorService.submit(new MyCallable());
        try {
            System.out.println(future.get());
        } catch (Exception e) {
            // nodo
        } finally {
            executorService.shutdown();
        }
    }
}

Future 表示一個(gè)可能還沒有完成的異步任務(wù)的結(jié)果,通過(guò) get 方法獲取執(zhí)行結(jié)果,該方法會(huì)阻塞直到任務(wù)返回結(jié)果。

三、FutureTask

FutureTask 實(shí)現(xiàn)了 RunnableFuture 接口,則 RunnableFuture 接口繼承了 Runnable 接口和 Future 接口,所以可以將 FutureTask 對(duì)象作為任務(wù)提交給 ThreadPoolExecutor 去執(zhí)行,也可以直接被 Thread 執(zhí)行;又因?yàn)閷?shí)現(xiàn)了 Future 接口,所以也能用來(lái)獲得任務(wù)的執(zhí)行結(jié)果。

FutureTask 構(gòu)造函數(shù):

public FutureTask(Callable callable)
public FutureTask(Runnable runnable, V result)

FutureTask 常用來(lái)封裝 CallableRunnable,可以作為一個(gè)任務(wù)提交到線程池中執(zhí)行。除了作為一個(gè)獨(dú)立的類之外,也提供了一些功能性函數(shù)供我們創(chuàng)建自定義 task 類使用。

FutureTask 線程安全由CAS來(lái)保證。

ExecutorService executor = Executors.newCachedThreadPool();
// FutureTask包裝callbale任務(wù),再交給線程池執(zhí)行
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
    System.out.println("子線程開始計(jì)算:");
    Integer sum = 0;
    for (int i = 1; i <= 100; i++)
        sum += i;
    return sum;
});

// 線程池執(zhí)行任務(wù), 運(yùn)行結(jié)果在 futureTask 對(duì)象里面
executor.submit(futureTask);

try {
    System.out.println("task運(yùn)行結(jié)果計(jì)算的總和為:" + futureTask.get());
} catch (Exception e) {
    e.printStackTrace();
}
executor.shutdown();

Callable 和 Future 的區(qū)別:Callable 用于產(chǎn)生結(jié)果,F(xiàn)uture 用于獲取結(jié)果

圖片

如果是對(duì)多個(gè)任務(wù)多次自由串行、或并行組合,涉及多個(gè)線程之間同步阻塞獲取結(jié)果,F(xiàn)uture 代碼實(shí)現(xiàn)會(huì)比較繁瑣,需要我們手動(dòng)處理各個(gè)交叉點(diǎn),很容易出錯(cuò)。

四、異步框架 CompletableFuture

Future 類通過(guò) get() 方法阻塞等待獲取異步執(zhí)行的運(yùn)行結(jié)果,性能比較差。

JDK1.8 中,Java 提供了 CompletableFuture 類,它是基于異步函數(shù)式編程。相對(duì)阻塞式等待返回結(jié)果,CompletableFuture 可以通過(guò)回調(diào)的方式來(lái)處理計(jì)算結(jié)果,實(shí)現(xiàn)了異步非阻塞,性能更優(yōu)。

優(yōu)點(diǎn)

  • 異步任務(wù)結(jié)束時(shí),會(huì)自動(dòng)回調(diào)某個(gè)對(duì)象的方法
  • 異步任務(wù)出錯(cuò)時(shí),會(huì)自動(dòng)回調(diào)某個(gè)對(duì)象的方法
  • 主線程設(shè)置好回調(diào)后,不再關(guān)心異步任務(wù)的執(zhí)行

泡茶示例:

圖片

(內(nèi)容摘自:極客時(shí)間的《Java 并發(fā)編程實(shí)戰(zhàn)》)

//任務(wù)1:洗水壺->燒開水
CompletableFuture<Void> f1 =
        CompletableFuture.runAsync(() -> {
            System.out.println("T1:洗水壺...");
            sleep(1, TimeUnit.SECONDS);

            System.out.println("T1:燒開水...");
            sleep(15, TimeUnit.SECONDS);
        });

//任務(wù)2:洗茶壺->洗茶杯->拿茶葉
CompletableFuture f2 =
        CompletableFuture.supplyAsync(() -> {
            System.out.println("T2:洗茶壺...");
            sleep(1, TimeUnit.SECONDS);

            System.out.println("T2:洗茶杯...");
            sleep(2, TimeUnit.SECONDS);

            System.out.println("T2:拿茶葉...");
            sleep(1, TimeUnit.SECONDS);
            return "龍井";
        });

//任務(wù)3:任務(wù)1和任務(wù)2完成后執(zhí)行:泡茶
CompletableFuture f3 =
        f1.thenCombine(f2, (__, tf) -> {
            System.out.println("T1:拿到茶葉:" + tf);
            System.out.println("T1:泡茶...");
            return "上茶:" + tf;
        });

//等待任務(wù)3執(zhí)行結(jié)果
System.out.println(f3.join());

}

CompletableFuture 提供了非常豐富的API,大約有50種處理串行,并行,組合以及處理錯(cuò)誤的方法。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3523

    瀏覽量

    93279
  • 程序
    +關(guān)注

    關(guān)注

    115

    文章

    3720

    瀏覽量

    80366
  • 異步
    +關(guān)注

    關(guān)注

    0

    文章

    62

    瀏覽量

    18017
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Spring Boot如何實(shí)現(xiàn)異步任務(wù)

    Spring Boot 提供了多種方式來(lái)實(shí)現(xiàn)異步任務(wù),這里介紹三種主要實(shí)現(xiàn)方式。 1、基于注解 @Async @Async 注解是 Spri
    的頭像 發(fā)表于 09-30 10:32 ?1151次閱讀

    【我是電子發(fā)燒友】低功耗設(shè)計(jì)的最佳編程模型:異步編程

    異步編程可以編寫出速度快、資源省的高效程序,可以在單線程環(huán)境下實(shí)現(xiàn)高并發(fā),可以在沒有操作系統(tǒng)的情況下實(shí)現(xiàn)TCP/IP等協(xié)議棧。又快又省可以將功耗控制在最低水平,因此
    發(fā)表于 04-29 20:30

    有哪幾種方式可以通過(guò)Keil模塊化編程實(shí)現(xiàn)流水燈設(shè)計(jì)?

    Keil的模塊化編程是什么?有哪幾種方式可以通過(guò)Keil模塊化編程實(shí)現(xiàn)流水燈設(shè)計(jì)?如何對(duì)流水燈設(shè)計(jì)進(jìn)行Proteus仿真?
    發(fā)表于 07-14 07:17

    芯片在線編程方式有哪些

    電路板編程,在線編程功能隨即流行起來(lái)。在線編程目前有ICP、ISP、IAP方式,各方式都是
    發(fā)表于 11-18 08:54

    幾種最基本的通訊方式解釋與總結(jié)

    幾種最基本的通訊方式解釋與總結(jié)出發(fā)點(diǎn)在讀書的時(shí),對(duì)于幾種通訊總是一知半解,沒有刻意的去學(xué)習(xí)使用,直到工作后,才發(fā)現(xiàn),對(duì)于知識(shí)的應(yīng)用不能只是一知半解,要想對(duì)其熟練應(yīng)用,就必須理解透徹。如果文中有寫的有
    發(fā)表于 02-23 07:30

    相機(jī)曝光模式有哪幾種種類?如何設(shè)置曝光模式?

    相機(jī)曝光模式有哪幾種種類?如何設(shè)置曝光模式?
    發(fā)表于 03-02 09:34

    異步收發(fā)通信端口(UART)的FPGA實(shí)現(xiàn)

    文章介紹了一種在現(xiàn)場(chǎng)可編程門陣列(FPGA)實(shí)現(xiàn)UART 的方法。首先闡述了UART 異步串行通信原理,然后介紹了實(shí)現(xiàn)UART
    發(fā)表于 08-06 16:24 ?55次下載

    異步傳輸方式的HDLC協(xié)議的實(shí)現(xiàn)

    研究實(shí)現(xiàn)了一種 HDLC (High Level Data Link Contr01)協(xié)議的改進(jìn)方法,該方法把HDLC協(xié)議傳統(tǒng)的同步傳榆方式改成了異步傳輸方式,既保留了原有HDLC協(xié)議
    發(fā)表于 07-20 17:25 ?62次下載
    <b class='flag-5'>異步</b>傳輸<b class='flag-5'>方式</b>的HDLC協(xié)議的<b class='flag-5'>實(shí)現(xiàn)</b>

    快速改變?yōu)V波器中心頻率的幾種實(shí)現(xiàn)方式

    快速改變?yōu)V波器中心頻率的幾種實(shí)現(xiàn)方式,下來(lái)看看
    發(fā)表于 01-07 21:24 ?12次下載

    在Python中實(shí)現(xiàn)異步編程(附源碼)

    異步編程是并行編程的一種方式。單個(gè)工作單元獨(dú)立于主應(yīng)用程序線程運(yùn)行,并通知調(diào)用線程其完成、失敗情況或進(jìn)度。下面這張圖理解起來(lái)會(huì)更直觀一些:
    的頭像 發(fā)表于 10-27 14:36 ?2299次閱讀
    在Python中<b class='flag-5'>實(shí)現(xiàn)</b><b class='flag-5'>異步</b><b class='flag-5'>編程</b>(附源碼)

    異步編程幾種種實(shí)現(xiàn)方式(下)

    除了硬編碼的異步編程處理方式,SpringBoot 框架還提供了 `注解式` 解決方案,以 `方法體` 為邊界,方法體內(nèi)部的代碼邏輯全部按異步方式
    的頭像 發(fā)表于 02-15 16:15 ?630次閱讀
    <b class='flag-5'>異步</b><b class='flag-5'>編程</b>的<b class='flag-5'>幾種種</b><b class='flag-5'>實(shí)現(xiàn)</b><b class='flag-5'>方式</b>(下)

    三相異步電動(dòng)機(jī)的幾種調(diào)速方式

    介紹了幾種調(diào)速的方式
    發(fā)表于 10-07 11:18 ?0次下載

    java實(shí)現(xiàn)多線程的幾種方式

    Java實(shí)現(xiàn)多線程的幾種方式 多線程是指程序中包含了兩個(gè)或以上的線程,每個(gè)線程都可以并行執(zhí)行不同的任務(wù)或操作。Java中的多線程可以提高程序的效率和性能,使得程序可以同時(shí)處理多個(gè)任務(wù)。 Java提供
    的頭像 發(fā)表于 03-14 16:55 ?441次閱讀

    PLC的編程方式編程語(yǔ)言

    在工業(yè)自動(dòng)化領(lǐng)域,PLC(Programmable Logic Controller,可編程邏輯控制器)因其強(qiáng)大的控制功能和靈活的編程方式而得到了廣泛應(yīng)用。PLC的編程
    的頭像 發(fā)表于 06-27 14:08 ?425次閱讀

    籠形異步電機(jī)常用的降壓?jiǎn)?dòng)方式

    ,保護(hù)電機(jī)和電網(wǎng)。 籠形異步電機(jī)降壓?jiǎn)?dòng)方式概述 籠形異步電機(jī)在啟動(dòng)時(shí),由于轉(zhuǎn)子的慣性和負(fù)載的影響,需要較大的啟動(dòng)轉(zhuǎn)矩。為了減少啟動(dòng)電流,通常采用以下幾種降壓?jiǎn)?dòng)
    的頭像 發(fā)表于 09-03 15:18 ?272次閱讀