什么?同步太慢了?为什么不试试异步处理呢?

前言

最近,我们公司有一个邮箱功能需求,需要去拉取收件箱的未读邮件,流程是这样的,连接到邮箱服务器 —》 拉取邮件 —》下载邮件中的附件 —》插入数据库 —》返回。

好的,这个流程看似没有什么问题,实际上却暗藏杀机。为啥?有的年轻人不讲武德,偷偷给你发送一封超大附件,这处理时间可就不得了了,会让前端一直等待,给用户的体验也非常不好,所以,我们需要对下载邮件做异步处理,在拉取完邮件之后直接插入数据库并返回即可,至于下载邮件,就另起线程去异步完成。

下面我们通过代码来看一下同步与异步的区别。

同步

public class SyncTest {

    //拉邮件
    private void pull(int i) {
        System.out.println("拉取一封邮件" + i);
    }

    //下载附件
    private void upload(int i) {
        System.out.println("下载附件" + i);
        // 模拟耗时操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 插入数据库
    private void insert(int i) {
        System.out.println("插入数据库" + i);
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        SyncTest syncTest = new SyncTest();
        for(int i = 0;i < 10;i++) {
            syncTest.pull(i);
            syncTest.upload(i);
            syncTest.insert(i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("处理的总时长:" + (endTime - startTime));
    }
}

这代码感觉像小学生写的流水账一样,没啥好说的,结果也如我们所愿,执行了很长很长一段时间。。。

拉取一封邮件0
下载附件0
插入数据库0
拉取一封邮件1
下载附件1
插入数据库1
拉取一封邮件2
下载附件2
插入数据库2
拉取一封邮件3
下载附件3
插入数据库3
拉取一封邮件4
下载附件4
插入数据库4
拉取一封邮件5
下载附件5
插入数据库5
拉取一封邮件6
下载附件6
插入数据库6
拉取一封邮件7
下载附件7
插入数据库7
拉取一封邮件8
下载附件8
插入数据库8
拉取一封邮件9
下载附件9
插入数据库9
处理的总时长:1020

异步

我们使用线程池,创建新线程去处理我们的耗时任务。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AsyncTest {

    //拉邮件
    private void pull(int i) {
        System.out.println("拉取一封邮件" + i);
    }

    //下载附件
    private void upload(int i) {
        System.out.println("下载附件" + i);
        // 模拟耗时操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 插入数据库
    private void insert(int i) {
        System.out.println("插入数据库" + i);
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        ExecutorService service = Executors.newFixedThreadPool(5);
        AsyncTest asyncTest = new AsyncTest();
        for(int i = 0;i < 10;i++) {
            asyncTest.pull(i);
            final int index = i;
            service.execute(() -> asyncTest.upload(index));
            asyncTest.insert(i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("处理的总时长:" + (endTime - startTime));
    }
}

显然,此时主线程与处理我们耗时任务的线程并不是同一个线程,主线程不执行耗时任务,直接返回;而线程池新建一个线程去处理我们的耗时任务。使用此方法大大降低了处理时间,不需执行耗时逻辑便可直接返回,提高了用户的体验。

结果如下,显然,执行时间要比同步的方式要快得多。

拉取一封邮件0
插入数据库0
拉取一封邮件1
插入数据库1
拉取一封邮件2
下载附件0
插入数据库2
拉取一封邮件3
插入数据库3
拉取一封邮件4
插入数据库4
拉取一封邮件5
插入数据库5
拉取一封邮件6
插入数据库6
拉取一封邮件7
插入数据库7
拉取一封邮件8
插入数据库8
拉取一封邮件9
插入数据库9
处理的总时长:57
下载附件1
下载附件2
下载附件3
下载附件4
下载附件5
下载附件6
下载附件7
下载附件8
下载附件9

更加简单的方式

如果我们使用 SpringBoot 的话,有更加简单的异步实现方式。只需要在想要异步执行的方法上加上 @Async 注解,然后在控制器和启动类上加上 @EnableAsync 这个注解即可。不过这里有一个点需要注意,异步方法不能在本类内进行调用,只能在本类之外进行调用。

有 @Async 注解的方法,会在默认的线程池中执行,从而达到异步执行的效果。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值