蚂蚁:实现异步的8种方式,你知道几个? 上

简介: 蚂蚁:实现异步的8种方式,你知道几个?上


一、🌈前言

异步执行对于开发者来说并不陌生,在实际的开发过程中,很多场景多会使用到异步,相比同步执行,异步可以大大缩短请求链路耗时时间,比如:「发送短信、邮件、异步更新等」 ,这些都是典型的可以通过异步实现的场景。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

二、异步的八种实现方式

  1. 线程Thread
  2. Future
  3. 异步框架CompletableFuture
  4. Spring注解@Async
  5. Spring ApplicationEvent事件
  6. 消息队列
  7. 第三方异步框架,比如Hutool的ThreadUtil
  8. Guava异步

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

三、什么是异步?

首先我们先看一个常见的用户下单的场景:

什么是异步

在同步操作中,我们执行到 「发送短信」 的时候,我们必须等待这个方法彻底执行完才能执行 「赠送积分」 这个操作,如果 「赠送积分」 这个动作执行时间较长,发送短信需要等待,这就是典型的同步场景。

实际上,发送短信和赠送积分没有任何的依赖关系,通过异步,我们可以实现赠送积分发送短信这两个操作能够同时进行,比如:

异步

这就是所谓的异步,是不是非常简单,下面就说说异步的几种实现方式吧。

四、异步编程

4.1 线程异步

public class AsyncThread extends Thread {
    @Override
    public void run() {
        System.out.println("Current thread name:" + Thread.currentThread().getName() + " Send email success!");
    }
    public static void main(String[] args) {
        AsyncThread asyncThread = new AsyncThread();
        asyncThread.run();
    }
}

当然如果每次都创建一个Thread线程,频繁的创建、销毁,浪费系统资源,我们可以采用线程池:

private ExecutorService executorService = Executors.newCachedThreadPool();
public void fun() {
    executorService.submit(new Runnable() {
        @Override
        public void run() {
            log.info("执行业务逻辑...");
        }
    });
}

可以将业务逻辑封装到RunnableCallable中,交由线程池来执行。

4.2 Future异步

@Slf4j
public class FutureManager {
    public String execute() throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        Future<String> future = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println(" --- task start --- ");
                Thread.sleep(3000);
                System.out.println(" --- task finish ---");
                return "this is future execute final result!!!";
            }
        });
        //这里需要返回值时会阻塞主线程
        String result = future.get();
        log.info("Future get result: {}", result);
        return result;
    }
    @SneakyThrows
    public static void main(String[] args) {
        FutureManager manager = new FutureManager();
        manager.execute();
    }
}

输出结果:

 --- task start --- 
 --- task finish ---
 Future get result: this is future execute final result!!!

4.2.1 Future的不足之处

Future的不足之处的包括以下几点:

1️⃣ 无法被动接收异步任务的计算结果:虽然我们可以主动将异步任务提交给线程池中的线程来执行,但是待异步任务执行结束之后,主线程无法得到任务完成与否的通知,它需要通过get方法主动获取任务执行的结果。2️⃣ Future件彼此孤立:有时某一个耗时很长的异步任务执行结束之后,你想利用它返回的结果再做进一步的运算,该运算也会是一个异步任务,两者之间的关系需要程序开发人员手动进行绑定赋予,Future并不能将其形成一个任务流(pipeline),每一个Future都是彼此之间都是孤立的,所以才有了后面的CompletableFuture,CompletableFuture就可以将多个Future串联起来形成任务流。3️⃣ Futrue没有很好的错误处理机制:截止目前,如果某个异步任务在执行发的过程中发生了异常,调用者无法被动感知,必须通过捕获get方法的异常才知晓异步任务执行是否出现了错误,从而在做进一步的判断处理。

4.3 CompletableFuture实现异步

public class CompletableFutureCompose {
    /**
     * thenAccept子任务和父任务公用同一个线程
     */
    @SneakyThrows
    public static void thenRunAsync() {
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });
        CompletableFuture<Void> cf2 = cf1.thenRunAsync(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something...");
        });
        //等待任务1执行完成
        System.out.println("cf1结果->" + cf1.get());
        //等待任务2执行完成
        System.out.println("cf2结果->" + cf2.get());
    }
    public static void main(String[] args) {
        thenRunAsync();
    }
}

我们不需要显式使用ExecutorService,CompletableFuture 内部使用了ForkJoinPool来处理异步任务,如果在某些业务场景我们想自定义自己的异步线程池也是可以的。

4.4 Spring的@Async异步

4.4.1 自定义异步线程池

/**
 * 线程池参数配置,多个线程池实现线程池隔离,@Async注解,默认使用系统自定义线程池,可在项目中设置多个线程池,在异步调用的时候,指明需要调用的线程池名称,比如:@Async("taskName")
@EnableAsync
@Configuration
public class TaskPoolConfig {
    /**
     * 自定义线程池
     *
     **/
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        //返回可用处理器的Java虚拟机的数量 12
        int i = Runtime.getRuntime().availableProcessors();
        System.out.println("系统最大线程数  : " + i);
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程池大小
        executor.setCorePoolSize(16);
        //最大线程数
        executor.setMaxPoolSize(20);
        //配置队列容量,默认值为Integer.MAX_VALUE
        executor.setQueueCapacity(99999);
        //活跃时间
        executor.setKeepAliveSeconds(60);
        //线程名字前缀
        executor.setThreadNamePrefix("asyncServiceExecutor -");
        //设置此执行程序应该在关闭时阻止的最大秒数,以便在容器的其余部分继续关闭之前等待剩余的任务完成他们的执行
        executor.setAwaitTerminationSeconds(60);
        //等待所有的任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

4.4.2 AsyncService

public interface AsyncService {
    MessageResult sendSms(String callPrefix, String mobile, String actionType, String content);
    MessageResult sendEmail(String email, String subject, String content);
}
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {
    @Autowired
    private IMessageHandler mesageHandler;
    @Override
    @Async("taskExecutor")
    public MessageResult sendSms(String callPrefix, String mobile, String actionType, String content) {
        try {
            Thread.sleep(1000);
            mesageHandler.sendSms(callPrefix, mobile, actionType, content);
        } catch (Exception e) {
            log.error("发送短信异常 -> ", e)
        }
    }
    @Override
    @Async("taskExecutor")
    public sendEmail(String email, String subject, String content) {
        try {
            Thread.sleep(1000);
            mesageHandler.sendsendEmail(email, subject, content);
        } catch (Exception e) {
            log.error("发送email异常 -> ", e)
        }
    }
}

在实际项目中, 使用@Async调用线程池,推荐等方式是是使用自定义线程池的模式,不推荐直接使用@Async直接实现异步。

相关文章
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
475 11
|
4月前
|
存储 安全 测试技术
2025年云真机与购买真机成本对比及高性价比平台推荐
文章对比了2025年云真机与购买实体机的成本,指出云真机在设备灵活性、批量部署和长期使用成本上更具优势,适合中高频测试场景。还介绍了主流云真机平台特点,给出筛选高性价比平台的逻辑及常见问题解答,助力企业按需选择。
|
6月前
|
机器学习/深度学习 人工智能 搜索推荐
拔俗AI学伴智能体系统:基于大模型与智能体架构的下一代个性化学习引擎
AI学伴智能体系统融合大模型、多模态理解与自主决策,打造具备思考能力的个性化学习伙伴。通过动态推理、长期记忆、任务规划与教学逻辑优化,实现千人千面的自适应教育,助力因材施教落地,推动教育公平与效率双提升。(238字)
759 0
|
负载均衡 网络协议 算法
一文读懂什么是Nginx?它能否实现IM的负载均衡?
Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡方案,从某种意义上来讲,Nginx几乎是低成本、高负载Web服务端代名词。 如此深入人心的Nginx,很多人也想当然的认为,在IM或消息推送等场景下是否也能使用Nginx来解决负载均衡问题? 另外,即时通讯网的论坛和QQ群里也经常有人问起,Nginx是否能支持TCP、UDP、WebSocket的负载
336 4
|
10月前
|
Go
理解 Go 语言中的 select 用法
本文深入解析了Go语言中`select`的用法,它类似于`switch case`,但仅用于通道(channel)的操作。文章通过多个示例说明了`select`的基本用法、避免死锁的方法、随机性特点以及如何实现超时机制。同时总结了`select`与`switch`的区别:`select`专用于通道操作,case执行是随机的,需注意死锁问题,且不支持`fallthrough`和函数表达式。
363 1
理解 Go 语言中的 select 用法
|
8月前
|
机器学习/深度学习 人工智能 运维
什么是ai智能?AI的九年飞跃史:从AlphaGo到Agent智能体
2025年,AI已深入生活与产业,从“大模型”到“智能体”,技术实现跃迁。智能体具备记忆、工具调用、任务规划与反馈能力,推动AI从“问答”走向“执行”。推理成本下降使AI平民化,落地场景集中在流程自动化与认知决策。但幻觉、责任归属与长程任务仍是挑战。未来将向多模态、端侧计算与联邦智能体发展。
529 0
|
人工智能
LangGraph:构建多代理动态工作流的开源框架,支持人工干预、循环、持久性等复杂工作流自动化
LangGraph 是一个基于图结构的开源框架,专为构建状态化、多代理系统设计,支持循环、持久性和人工干预,适用于复杂的工作流自动化。
2376 12
LangGraph:构建多代理动态工作流的开源框架,支持人工干预、循环、持久性等复杂工作流自动化
|
SQL 缓存 Java
框架源码私享笔记(02)Mybatis核心框架原理 | 一条SQL透析核心组件功能特性
本文详细解构了MyBatis的工作机制,包括解析配置、创建连接、执行SQL、结果封装和关闭连接等步骤。文章还介绍了MyBatis的五大核心功能特性:支持动态SQL、缓存机制(一级和二级缓存)、插件扩展、延迟加载和SQL注解,帮助读者深入了解其高效灵活的设计理念。
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
测试技术 uml 开发者
使用UML进行系统建模:深入解析与实践指南
【8月更文挑战第19天】UML作为一种强大的建模语言,为系统建模提供了全面的支持。通过合理使用UML,可以显著提高软件开发的效率和质量,促进团队成员之间的有效沟通。然而,UML并非万能,它需要根据项目的具体情况进行灵活应用和调整。希望本文能为你在使用UML进行系统建模时提供一些有益的参考和指导。

热门文章

最新文章