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 ? 次閱讀

五、 SpringBoot 注解 @Async

除了硬編碼的異步編程處理方式,SpringBoot 框架還提供了 注解式 解決方案,以 方法體 為邊界,方法體內(nèi)部的代碼邏輯全部按異步方式執(zhí)行。

首先,使用 @EnableAsync 啟用異步注解

@SpringBootApplication
@EnableAsync
public class StartApplication {

    public static void main(String[] args) {
        SpringApplication.run(StartApplication.class, args);
    }
}

自定義線程池:

@Configuration
@Slf4j
public class ThreadPoolConfiguration {

    @Bean(name = "defaultThreadPoolExecutor", destroyMethod = "shutdown")
    public ThreadPoolExecutor systemCheckPoolExecutorService() {

        return new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS,
                new LinkedBlockingQueue(10000),
                new ThreadFactoryBuilder().setNameFormat("default-executor-%d").build(),
                (r, executor) -> log.error("system pool is full! "));
    }
}

在異步處理的方法上添加注解 @Async ,當(dāng)對(duì) execute 方法 調(diào)用時(shí),通過自定義的線程池 defaultThreadPoolExecutor 異步化執(zhí)行 execute 方法

@Service
public class AsyncServiceImpl implements AsyncService {

    @Async("defaultThreadPoolExecutor")
    public Boolean execute(Integer num) {
        System.out.println("線程:" + Thread.currentThread().getName() + " , 任務(wù):" + num);
        return true;
    }

}

用 @Async 注解標(biāo)記的方法,稱為異步方法。在spring boot應(yīng)用中使用 @Async 很簡(jiǎn)單:

  • 調(diào)用異步方法類上或者啟動(dòng)類加上注解 @EnableAsync
  • 在需要被異步調(diào)用的方法外加上 @Async
  • 所使用的 @Async 注解方法的類對(duì)象應(yīng)該是Spring容器管理的bean對(duì)象;

六、Spring ApplicationEvent 事件

事件機(jī)制在一些大型項(xiàng)目中被經(jīng)常使用,Spring 專門提供了一套事件機(jī)制的接口,滿足了架構(gòu)原則上的解耦。

ApplicationContext 通過 ApplicationEvent 類和 ApplicationListener 接口進(jìn)行事件處理。如果將實(shí)現(xiàn) ApplicationListener 接口的 bean 注入到上下文中,則每次使用 ApplicationContext 發(fā)布 ApplicationEvent 時(shí),都會(huì)通知該 bean。本質(zhì)上,這是標(biāo)準(zhǔn)的觀察者設(shè)計(jì)模式。

ApplicationEvent 是由 Spring 提供的所有 Event 類的基類

首先,自定義業(yè)務(wù)事件子類,繼承自 ApplicationEvent,通過泛型注入業(yè)務(wù)模型參數(shù)類。相當(dāng)于 MQ 的消息體。

public class OrderEvent extends AbstractGenericEvent {
    public OrderEvent(OrderModel source) {
        super(source);
    }
}

然后,編寫事件監(jiān)聽器。ApplicationListener 接口是由 Spring 提供的事件訂閱者必須實(shí)現(xiàn)的接口,我們需要定義一個(gè)子類,繼承 ApplicationListener。相當(dāng)于 MQ 的消費(fèi)端

@Component
public class OrderEventListener implements ApplicationListener<OrderEvent> {
    @Override
    public void onApplicationEvent(OrderEvent event) {

        System.out.println("【OrderEventListener】監(jiān)聽器處理!" + JSON.toJSONString(event.getSource()));

    }
}

最后,發(fā)布事件,把某個(gè)事件告訴所有與這個(gè)事件相關(guān)的監(jiān)聽器。相當(dāng)于 MQ 的生產(chǎn)端。

OrderModel orderModel = new OrderModel();
orderModel.setOrderId((long) i);
orderModel.setBuyerName("Tom-" + i);
orderModel.setSellerName("judy-" + i);
orderModel.setAmount(100L);
// 發(fā)布Spring事件通知
SpringUtils.getApplicationContext().publishEvent(new OrderEvent(orderModel));

加個(gè)餐:

[消費(fèi)端]線程:http-nio-8090-exec-1,消費(fèi)事件 {"amount":100.0,"buyerName":"Tom-1","orderId":1,"sellerName":"judy-1"}
[生產(chǎn)端]線程:http-nio-8090-exec-1,發(fā)布事件 1
[消費(fèi)端]線程:http-nio-8090-exec-1,消費(fèi)事件 {"amount":100.0,"buyerName":"Tom-2","orderId":2,"sellerName":"judy-2"}
[生產(chǎn)端]線程:http-nio-8090-exec-1,發(fā)布事件 2
[消費(fèi)端]線程:http-nio-8090-exec-1,消費(fèi)事件 {"amount":100.0,"buyerName":"Tom-3","orderId":3,"sellerName":"judy-3"}
[生產(chǎn)端]線程:http-nio-8090-exec-1,發(fā)布事件 3

上面是跑了個(gè)demo的運(yùn)行結(jié)果,我們發(fā)現(xiàn)無(wú)論生產(chǎn)端還是消費(fèi)端,使用了同一個(gè)線程 http-nio-8090-exec-1,Spring 框架的事件機(jī)制默認(rèn)是同步阻塞的。只是在代碼規(guī)范方面做了解耦,有較好的擴(kuò)展性,但底層還是采用同步調(diào)用方式。

那么問題來(lái)了,如果想實(shí)現(xiàn)異步調(diào)用,如何處理?

我們需要手動(dòng)創(chuàng)建一個(gè) SimpleApplicationEventMulticaster,并設(shè)置 TaskExecutor,此時(shí)所有的消費(fèi)事件采用異步線程執(zhí)行。

@Component
public class SpringConfiguration {

    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster(@Qualifier("defaultThreadPoolExecutor") ThreadPoolExecutor defaultThreadPoolExecutor) {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(defaultThreadPoolExecutor);
        return simpleApplicationEventMulticaster;
    }

}

我們看下改造后的運(yùn)行結(jié)果:

[生產(chǎn)端]線程:http-nio-8090-exec-1,發(fā)布事件 1
[生產(chǎn)端]線程:http-nio-8090-exec-1,發(fā)布事件 2
[生產(chǎn)端]線程:http-nio-8090-exec-1,發(fā)布事件 3
[消費(fèi)端]線程:default-executor-1,消費(fèi)事件 {"amount":100.0,"buyerName":"Tom-2","orderId":2,"sellerName":"judy-2"}
[消費(fèi)端]線程:default-executor-2,消費(fèi)事件 {"amount":100.0,"buyerName":"Tom-1","orderId":1,"sellerName":"judy-1"}
[消費(fèi)端]線程:default-executor-0,消費(fèi)事件 {"amount":100.0,"buyerName":"Tom-3","orderId":3,"sellerName":"judy-3"}

SimpleApplicationEventMulticaster 這個(gè)我們自己實(shí)例化的 Bean 與系統(tǒng)默認(rèn)的加載順序如何?會(huì)不會(huì)有沖突?

查了下 Spring 源碼,處理邏輯在 AbstractApplicationContext#initApplicationEventMulticaster 方法中,通過 beanFactory 查找是否有自定義的 Bean,如果沒有,容器會(huì)自己 new 一個(gè) SimpleApplicationEventMulticaster 對(duì)象注入到容器中。

圖片

代碼地址:https://github.com/aalansehaiyang/wx-project

七、消息隊(duì)列

異步架構(gòu)是互聯(lián)網(wǎng)系統(tǒng)中一種典型架構(gòu)模式,與同步架構(gòu)相對(duì)應(yīng)。而消息隊(duì)列天生就是這種異步架構(gòu),具有超高吞吐量和超低時(shí)延。

消息隊(duì)列異步架構(gòu)的主要角色包括消息生產(chǎn)者、消息隊(duì)列和消息消費(fèi)者。

圖片

消息生產(chǎn)者就是主應(yīng)用程序,生產(chǎn)者將調(diào)用請(qǐng)求封裝成消息發(fā)送給消息隊(duì)列。

消息隊(duì)列的職責(zé)就是緩沖消息,等待消費(fèi)者消費(fèi)。根據(jù)消費(fèi)方式又分為點(diǎn)對(duì)點(diǎn)模式發(fā)布訂閱模式兩種。

消息消費(fèi)者,用來(lái)從消息隊(duì)列中拉取、消費(fèi)消息,完成業(yè)務(wù)邏輯處理。

當(dāng)然市面上消息隊(duì)列框架非常多,常見的有RabbitMQ、Kafka、RocketMQ、ActiveMQ 和 Pulsar 等

圖片

不同的消息隊(duì)列的功能特性會(huì)略有不同,但整體架構(gòu)類似,這里就不展開了。

我們只需要記住一個(gè)關(guān)鍵點(diǎn),借助消息隊(duì)列這個(gè)中間件可以高效的實(shí)現(xiàn)異步編程。

聲明:本文內(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

    瀏覽量

    93278
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4672

    瀏覽量

    67781
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    335

    瀏覽量

    14259
收藏 人收藏

    評(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)
    發(fā)表于 04-29 20:30

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

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

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

    問題的地方,麻煩各位及時(shí)反饋一,謝謝。在對(duì)幾種協(xié)議進(jìn)行介紹之前,先介紹三個(gè)基本概念:(1)異步和同步假設(shè)現(xiàn)在有兩臺(tái)設(shè)備A、B之間需要盡心通信,如果A與B兩臺(tái)設(shè)備使用的是同一個(gè)時(shí)鐘信號(hào),則稱為同步,如果使用的不是同一個(gè)時(shí)鐘信號(hào),
    發(fā)表于 02-23 07:30

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

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

    Modbus通訊協(xié)議的幾種實(shí)現(xiàn)方式

    Modbus通訊的方式   針對(duì)Modbus的串口和TCP兩種不同的方式,在LabVIEW中通常可以通過以下幾種方法實(shí)現(xiàn)Modbus通訊。其中一些實(shí)
    發(fā)表于 05-05 16:19

    請(qǐng)問一plc可以實(shí)現(xiàn)無(wú)線通信嗎?有幾種方式?

    請(qǐng)問一plc可以實(shí)現(xiàn)無(wú)線通信嗎?有幾種方式
    發(fā)表于 05-09 17:23

    異步傳輸方式的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)方式(上)

    異步編程是讓程序并發(fā)運(yùn)行的一種手段。它允許多個(gè)事件同時(shí)發(fā)生,當(dāng)程序調(diào)用需要長(zhǎng)時(shí)間運(yùn)行的方法時(shí),它不會(huì)阻塞當(dāng)前的執(zhí)行流程,程序可以繼續(xù)運(yùn)行。
    的頭像 發(fā)表于 02-15 16:15 ?650次閱讀
    <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 ?271次閱讀