Springboot中的@EnableAsync和@Async的作用和基本用法

摘要:
2.注释@EnableAsync和@Async用于实现第一个方法。虽然达到了我们想要的结果,但我们发现,如果我们在多个请求中需要这个异步请求,我们需要每次都编写这样的冗余线程池配置。因此spring使用@EnableAsync启用异步支持,使用@Async异步执行方法,以提高开发人员的开发效率。

  日常开发中,我们偶尔会遇到在业务层中需要同时修改多张表的数据并需要有序的执行,如果用往常的同步的方式,也就是单线程的方式来执行的话,可能会出现执行超时等异常造成请求结果失败,及时成功,前端也需要等待较长时间来获取响应结果,这样不但造成了用户体验差,而且会经常出现请求执行失败的问题,在这里我们一般会采用3种方式来处理。

在采用三种方式之前,我们所有来观察一下使用同步的方式实现的结果:

1.我们定义一个AsyncController,如下所示:

@Api(value = "异步测试",tags = "异步测试")
@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;
    /**
     * 使用传统方式测试
     */
    @GetMapping("/test")
    @ApiOperation(value = "测试异步处理")
    public void test() {
        System.out.println("获取主线程名称:" + Thread.currentThread().getName());
        asyncService.execute();
        System.out.println("执行成功,返回结果");
    }
}

2. 我们定义AsyncService,如下所示:

@Service
public class AsyncService {
    public void execute() {
        // 这里执行实际的业务逻辑,在这里我们就是用一个简单的遍历来模拟
        Arrays.stream(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}).forEach(t -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("获取number为:" + t);
        });
    }
}

执行请求http://localhost:8889/test,结果显示如下:  

获取主线程名称:http-nio-8889-exec-4
获取number为:1
获取number为:2
获取number为:3
获取number为:4
获取number为:5
获取number为:6
获取number为:7
获取number为:8
获取number为:9
获取number为:10
执行成功,返回结果

接下来我们采用我们所说的3种方式来实现: 

1.使用线程池的方式来实现

修改AsyncController中的test()方法,service层不变,如下所示:  

@Api(value = "异步测试",tags = "异步测试")
@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;
    /**
     * 使用传统方式测试
     */
    @GetMapping("/test")
    @ApiOperation(value = "测试异步处理")
    public void test() {
        System.out.println("获取主线程名称:" + Thread.currentThread().getName());
        /**
         * 定义一个线程池
         * 核心线程数(corePoolSize):1
         * 最大线程数(maximumPoolSize): 1
         * 保持连接时间(keepAliveTime):50000
         * 时间单位 (TimeUnit):TimeUnit.MILLISECONDS(毫秒)
         * 阻塞队列 new LinkedBlockingQueue<Runnable>()
         */
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1,5,50000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
        // 执行业务逻辑方法serviceTest()
        executor.execute(new Runnable() {
            @Override
            public void run() {
                asyncService.execute();
            }
        });
        System.out.println("执行成功,返回结果");
    }
}

执行请求http://localhost:8889/test,结果显示如下:  

获取主线程名称:http-nio-8889-exec-1
执行成功,返回结果
获取number为:1
获取number为:2
获取number为:3
获取number为:4
获取number为:5
获取number为:6
获取number为:7
获取number为:8
获取number为:9
获取number为:10

我们发现在主线程中使用线程池中的线程来实现,程序的执行结果表明,主线程直接将结果进行了返回,然后才是线程池在执行业务逻辑,减少了请求响应时长。  

2. 使用注解@EnableAsync和@Async来实现 

  第一种方式虽然实现了我们想要的结果,但是,我们发现如果我们在多个请求中都需要这种异步请求,每次都要写这么冗余的线程池配置,所以spring为了提升开发人员的开发效率,使用@EnableAsync来开启异步的支持,使用@Async来对某个方法进行异步执行。AsyncController如下所示:

@Api(value = "异步测试",tags = "异步测试")
@RestController
public class AsyncController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping("/test")
    @ApiOperation(value = "测试异步处理")
    public void test() {
        System.out.println("获取主线程名称:" + Thread.currentThread().getName());
        asyncService.execute();
        System.out.println("执行成功,返回结果");
    }
}

AsyncService如下所示: 

@Service
@EnableAsync
public class AsyncService {
    /**
     * 采用异步执行
     */
    @Async
    public void execute() {
        // 这里执行实际的业务逻辑,在这里我们就是用一个简单的遍历来模拟
        Arrays.stream(new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}).forEach(t -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("获取number为:" + t);
        });
    }
}

执行请求http://localhost:8889/test,结果显示如下:

获取主线程名称:http-nio-8889-exec-1
执行成功,返回结果
获取number为:1
获取number为:2
获取number为:3
获取number为:4
获取number为:5
获取number为:6
获取number为:7
获取number为:8
获取number为:9
获取number为:10

结果与使用线程池的结果一致,简化了我们编写代码的逻辑,@EnableAsync和@Async注解帮我们实现了避免创建线程池的繁琐,提高了我们的开发效率,@EnableAsync也可以写在启动类上。 

3. 使用消息队列(mq)来实现  

  当我们涉及的请求在业务逻辑中一次性操作很多很多的数据,例如:一个请求执行业务操作后,将操作日志插入到数据库中,我们可以使用@Async来实现,但是这样增加了业务和非业务关系的冗余性,同时如何并发量很大,我们使用@Async处理,一旦服务器宕机,尚未处理的任务会尽数丢失,为了避免这种情况,我们会采用mq来实现,将业务逻辑和非业务逻辑进行隔离执行,互不影响,非业务逻辑不会影响到执行业务逻辑的结果和主机性能,对于mq的处理,我会单独写一篇文章来详细介绍。

文章地址:xxxxx

免责声明:文章转载自《Springboot中的@EnableAsync和@Async的作用和基本用法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇LVS分析CentOS7使用Alien将RPM从DE转换为DEB和DEB转换为RPM包下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

mysql 实现事务的提交与回滚

最近要对数据库的数据进行一个定时迁移,为了防止在执行过程sql语句因为某些原因报错而导致数据转移混乱,因此要对我们的脚本加以事务进行控制。 首先我们建一张tran_test表 CREATE TABLE tran_test( f1 VARCHAR(10) NOT NULL, f2 INT(1) DEFAULT NULL, PRIMARY K...

记录yii2-imagine几个常用方法

记录yii2-imagine几个常用方法:      //压缩 Image::thumbnail('@webroot/img/test-image.jpg', 120, 120)->save(Yii::getAlias('@webroot/img/thumb-test-image.jpg'), ['quality' => 100...

流式实时分布式计算的设计

https://blog.csdn.net/anzhsoft/article/details/38168025 1. 流式计算的背景和特点 现在很多公司每天都会产生数以TB级的大数据,如何对这些数据进行挖掘,分析成了很重要的课题。比如: 电子商务:需要处理并且挖掘用户行为产生的数据,产生推荐,从而带来更多的流量和收益。最理想的推荐就是根据兴趣推荐给用户本来...

(一)SQL注入漏洞测试的方式总结

一、工具注入 1.SQLMap的作用 判断可注入的参数 判断可以用那种SQL注入技术来注入 识别出哪种数据库 根据用户选择,读取哪些数据(库、表、列、字段值...) 2.注入技术 【A】基于布尔的盲注(Boolean-Based Blind Injection): 可以根据返回页面判断条件真假的注入 【B】基于时间的盲注(Time-Based Bl...

为什么要用Volley中的RequestFuture封装RxJava来用异步请求处理同步请求?

前几天健哥喊我研究一下RvJava,在网络请求用,更简洁更有条理,然后就会抽空研究研究,现在项目里网络库是Volley,就结合项目和网上的demo看,突然脑袋蹦出这个问题,现在看起来这个问题有一点蠢蠢的。 firstly,名词解释一下。 Volley是谷歌爸爸给咱们封装好了的网络请求库,帮我们封装了具体的请求,线程切换以及数据转换,适合短小多并发的网络请求...

浅谈常见的特征选择方法

这只狗子越来越懒,大家可以直接看 notebook 版本的代码和结果 https://gitee.com/dogecheng/python/blob/master/machine_learning/%E7%89%B9%E5%BE%81%E9%80%89%E6%8B%A9.ipynb 这篇文章是“阉割”版,主要是分类任务的特征选择,不完全适用于回归任务,具体...