0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

异步编程的几种种实现方式(下)

jf_78858299 来源:微观技术 作者:Tom哥 2023-02-15 16:15 次阅读

五、 SpringBoot 注解 @Async

除了硬编码的异步编程处理方式,SpringBoot 框架还提供了 注解式 解决方案,以 方法体 为边界,方法体内部的代码逻辑全部按异步方式执行。

首先,使用 @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 ,当对 execute 方法 调用时,通过自定义的线程池 defaultThreadPoolExecutor 异步化执行 execute 方法

@Service
public class AsyncServiceImpl implements AsyncService {

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

}

用 @Async 注解标记的方法,称为异步方法。在spring boot应用中使用 @Async 很简单:

  • 调用异步方法类上或者启动类加上注解 @EnableAsync
  • 在需要被异步调用的方法外加上 @Async
  • 所使用的 @Async 注解方法的类对象应该是Spring容器管理的bean对象;

六、Spring ApplicationEvent 事件

事件机制在一些大型项目中被经常使用,Spring 专门提供了一套事件机制的接口,满足了架构原则上的解耦。

ApplicationContext 通过 ApplicationEvent 类和 ApplicationListener 接口进行事件处理。如果将实现 ApplicationListener 接口的 bean 注入到上下文中,则每次使用 ApplicationContext 发布 ApplicationEvent 时,都会通知该 bean。本质上,这是标准的观察者设计模式

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

首先,自定义业务事件子类,继承自 ApplicationEvent,通过泛型注入业务模型参数类。相当于 MQ 的消息体。

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

然后,编写事件监听器。ApplicationListener 接口是由 Spring 提供的事件订阅者必须实现的接口,我们需要定义一个子类,继承 ApplicationListener。相当于 MQ 的消费端

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

        System.out.println("【OrderEventListener】监听器处理!" + JSON.toJSONString(event.getSource()));

    }
}

最后,发布事件,把某个事件告诉所有与这个事件相关的监听器。相当于 MQ 的生产端。

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

加个餐:

[消费端]线程:http-nio-8090-exec-1,消费事件 {"amount":100.0,"buyerName":"Tom-1","orderId":1,"sellerName":"judy-1"}
[生产端]线程:http-nio-8090-exec-1,发布事件 1
[消费端]线程:http-nio-8090-exec-1,消费事件 {"amount":100.0,"buyerName":"Tom-2","orderId":2,"sellerName":"judy-2"}
[生产端]线程:http-nio-8090-exec-1,发布事件 2
[消费端]线程:http-nio-8090-exec-1,消费事件 {"amount":100.0,"buyerName":"Tom-3","orderId":3,"sellerName":"judy-3"}
[生产端]线程:http-nio-8090-exec-1,发布事件 3

上面是跑了个demo的运行结果,我们发现无论生产端还是消费端,使用了同一个线程 http-nio-8090-exec-1,Spring 框架的事件机制默认是同步阻塞的。只是在代码规范方面做了解耦,有较好的扩展性,但底层还是采用同步调用方式。

那么问题来了,如果想实现异步调用,如何处理?

我们需要手动创建一个 SimpleApplicationEventMulticaster,并设置 TaskExecutor,此时所有的消费事件采用异步线程执行。

@Component
public class SpringConfiguration {

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

}

我们看下改造后的运行结果:

[生产端]线程:http-nio-8090-exec-1,发布事件 1
[生产端]线程:http-nio-8090-exec-1,发布事件 2
[生产端]线程:http-nio-8090-exec-1,发布事件 3
[消费端]线程:default-executor-1,消费事件 {"amount":100.0,"buyerName":"Tom-2","orderId":2,"sellerName":"judy-2"}
[消费端]线程:default-executor-2,消费事件 {"amount":100.0,"buyerName":"Tom-1","orderId":1,"sellerName":"judy-1"}
[消费端]线程:default-executor-0,消费事件 {"amount":100.0,"buyerName":"Tom-3","orderId":3,"sellerName":"judy-3"}

SimpleApplicationEventMulticaster 这个我们自己实例化的 Bean 与系统默认的加载顺序如何?会不会有冲突?

查了下 Spring 源码,处理逻辑在 AbstractApplicationContext#initApplicationEventMulticaster 方法中,通过 beanFactory 查找是否有自定义的 Bean,如果没有,容器会自己 new 一个 SimpleApplicationEventMulticaster 对象注入到容器中。

图片

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

七、消息队列

异步架构是互联网系统中一种典型架构模式,与同步架构相对应。而消息队列天生就是这种异步架构,具有超高吞吐量和超低时延。

消息队列异步架构的主要角色包括消息生产者、消息队列和消息消费者。

图片

消息生产者就是主应用程序,生产者将调用请求封装成消息发送给消息队列。

消息队列的职责就是缓冲消息,等待消费者消费。根据消费方式又分为点对点模式发布订阅模式两种。

消息消费者,用来从消息队列中拉取、消费消息,完成业务逻辑处理。

当然市面上消息队列框架非常多,常见的有RabbitMQ、Kafka、RocketMQ、ActiveMQ 和 Pulsar 等

图片

不同的消息队列的功能特性会略有不同,但整体架构类似,这里就不展开了。

我们只需要记住一个关键点,借助消息队列这个中间件可以高效的实现异步编程。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3578

    浏览量

    93551
  • 代码
    +关注

    关注

    30

    文章

    4733

    浏览量

    68294
  • spring
    +关注

    关注

    0

    文章

    338

    浏览量

    14301
收藏 人收藏

    评论

    相关推荐

    Spring Boot如何实现异步任务

    Spring Boot 提供了多种方式实现异步任务,这里介绍三种主要实现方式。 1、基于注解 @Async @Async 注解是 Spri
    的头像 发表于 09-30 10:32 1392次阅读

    USART异步工作方式编程

    USART异步工作方式编程 串行通信的接收有查询和中断2种方式,在实际应用中,一般不采用查询接收数据,常用的是中断接收数据。发送有中断发送和非中断发送,在下面的例程中我们采用了中断接收
    发表于 09-08 09:48

    【我是电子发烧友】低功耗设计的最佳编程模型:异步编程

    异步编程可以编写出速度快、资源省的高效程序,可以在单线程环境实现高并发,可以在没有操作系统的情况实现
    发表于 04-29 20:30

    有哪几种方式可以通过Keil模块化编程实现流水灯设计?

    Keil的模块化编程是什么?有哪几种方式可以通过Keil模块化编程实现流水灯设计?如何对流水灯设计进行Proteus仿真?
    发表于 07-14 07:17

    几种最基本的通讯方式解释与总结

    问题的地方,麻烦各位及时反馈一,谢谢。在对几种协议进行介绍之前,先介绍三个基本概念:(1)异步和同步假设现在有两台设备A、B之间需要尽心通信,如果A与B两台设备使用的是同一个时钟信号,则称为同步,如果使用的不是同一个时钟信号,
    发表于 02-23 07:30

    相机曝光模式有哪几种种类?如何设置曝光模式?

    相机曝光模式有哪几种种类?如何设置曝光模式?
    发表于 03-02 09:34

    Modbus通讯协议的几种实现方式

    Modbus通讯的方式   针对Modbus的串口和TCP两种不同的方式,在LabVIEW中通常可以通过以下几种方法实现Modbus通讯。其中一些
    发表于 05-05 16:19

    请问一plc可以实现无线通信吗?有几种方式

    请问一plc可以实现无线通信吗?有几种方式
    发表于 05-09 17:23

    异步传输方式的HDLC协议的实现

    研究实现了一种 HDLC (High Level Data Link Contr01)协议的改进方法,该方法把HDLC协议传统的同步传榆方式改成了异步传输方式,既保留了原有HDLC协议
    发表于 07-20 17:25 62次下载
    <b class='flag-5'>异步</b>传输<b class='flag-5'>方式</b>的HDLC协议的<b class='flag-5'>实现</b>

    快速改变滤波器中心频率的几种实现方式

    快速改变滤波器中心频率的几种实现方式,下来看看
    发表于 01-07 21:24 12次下载

    在Python中实现异步编程(附源码)

    异步编程是并行编程的一种方式。单个工作单元独立于主应用程序线程运行,并通知调用线程其完成、失败情况或进度。下面这张图理解起来会更直观一些:
    的头像 发表于 10-27 14:36 2328次阅读
    在Python中<b class='flag-5'>实现</b><b class='flag-5'>异步</b><b class='flag-5'>编程</b>(附源码)

    异步编程几种种实现方式(上)

    异步编程是让程序并发运行的一种手段。它允许多个事件同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行。
    的头像 发表于 02-15 16:15 714次阅读
    <b class='flag-5'>异步</b><b class='flag-5'>编程</b>的<b class='flag-5'>几种种</b><b class='flag-5'>实现</b><b class='flag-5'>方式</b>(上)

    三相异步电动机的几种调速方式

    介绍了几种调速的方式
    发表于 10-07 11:18 0次下载

    java实现多线程的几种方式

    Java实现多线程的几种方式 多线程是指程序中包含了两个或以上的线程,每个线程都可以并行执行不同的任务或操作。Java中的多线程可以提高程序的效率和性能,使得程序可以同时处理多个任务。 Java提供
    的头像 发表于 03-14 16:55 557次阅读

    笼形异步电机常用的降压启动方式

    ,保护电机和电网。 笼形异步电机降压启动方式概述 笼形异步电机在启动时,由于转子的惯性和负载的影响,需要较大的启动转矩。为了减少启动电流,通常采用以下几种降压启动
    的头像 发表于 09-03 15:18 1062次阅读