0
点赞
收藏
分享

微信扫一扫

使用@Async注解来实现异步操作(简化了多线程实现异步操作)

佳简诚锄 2022-03-11 阅读 112
java
  • 使用@Async注解来实现异步操作(不需要等待的,都是同时执行的,不用等待第一个任务结束以后在调用后面的任务),这里假设一个场景,比如导出excel表格,数据量非常的大,比如有有一千万数据,导出的时候有时候会出现连接超时,数据还没有导出完。这里我们就用异步去导出这个excel,我们还是正常的返回,但是导出放在后台让他自己运行就行了。

  • 使用@Async注解其实就是简化了多线程的操作,多线程也是可以达到这种效果的,多个人任务同时执行,就不会把时间浪费在等待的时间上面去了

  • 这里我贴上别人的解释
    在我们使用spring框架的过程中,在很多时候我们会使用@async注解来异步执行某一些方法,提高系统的执行效率。今天我们来探讨下 spring 是如何完成这个功能的。

    spring 在扫描bean的时候会扫描方法上是否包含@async的注解,如果包含的,spring会为这个bean动态的生成一个子类,我们称之为代理类(?),代理类是继承我们所写的bean的,然后把代理类注入进来,那此时,在执行此方法的时候,会到代理类中,代理类判断了此方法需要异步执行,就不会调用父类(我们原本写的bean)的对应方法。spring自己维护了一个队列,他会把需要执行的方法,放入队列中,等待线程池去读取这个队列,完成方法的执行,从而完成了异步的功能。我们可以关注到再配置task的时候,是有参数让我们配置线程池的数量的。因为这种实现方法,所以在同一个类中的方法调用,添加@async注解是失效的!,原因是当你在同一个类中的时候,方法调用是在类体内执行的,spring无法截获这个方法调用。

    那在深入一步,spring为我们提供了AOP,面向切面的功能。他的原理和异步注解的原理是类似的,spring在启动容器的时候,会扫描切面所定义的类。在这些类被注入的时候,所注入的也是代理类,当你调用这些方法的时候,本质上是调用的代理类。通过代理类再去执行父类相对应的方法,那spring只需要在调用之前和之后执行某段代码就完成了AOP的实现了!

  • 下面时具体的实现代码

  • 首先我们先定义一个线程池,异步操作也是需要线程池的

package com.zhou.demo.threadpool;

import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;

/**
 * @author Mr.zhou
 * 定义线程池
 */
@Component
@EnableAsync
public class ThreadExecutorConfig {

    private static final int corePoolSize = 10;

    private static final int maxPoolSize = 200;

    private static final int queueCapacity = 10;

    @Bean
    public static Executor smartExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize); // 线程池的核心大小
        executor.setMaxPoolSize(maxPoolSize);   // 线程池的最大数量
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("smartExecutor"); //线程池的名称  可以自己定义
        executor.initialize();
        return executor;
    }
}

  • 业务需求,需要执行的任务,记得加上@Component
package com.zhou.demo.threadpool;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.Random;

/**
 * @author Mr.zhou
 */
// Component 将这个类加载到spring容器当中  Slf4j是springboot自带的日志框架
@Component
@Slf4j
public class AsyncTask {

    // 生成一个随机数
    private static Random random = new Random();

    // 开启异步操作
    @Async
    public void task() throws InterruptedException {
        System.out.println("开始做任务一");
        long start = System.currentTimeMillis();
        // 让这个线程睡眠,让测试效果更加的明显
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
    }
    // 开启异步操作
    @Async
    public void taskAsync1() throws Exception {
        // 让这个线程睡眠,让测试效果更加的明显
        Thread.sleep(1000);
        for (int i = 0; i < 10; i++) {
            log.info("##########################" + i);
        }
    }
}

  • 使用Controller进行调用实现层
package com.zhou.demo.threadpool;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.Executor;

/**
 * @author Mr.zhou
 */
@RestController
@RequestMapping("api/task")
@Slf4j
public class TaskController{

    /**
     * 这里使用构造函数注入,spring官方文档推荐的
     * 也可以用注解的形式注入
     */
    private AsyncTask asyncTask;
    public TaskController(AsyncTask asyncTask) {
        this.asyncTask = asyncTask;
    }

    @RequestMapping(value = "/async", method = RequestMethod.GET)
    public String task1() throws Exception {
        log.info("test async................");
        asyncTask.task();
        asyncTask.taskAsync1();
        return "执行成功";

    }
}
  • 当我们请求接口的时候:localhost:8080/api/task/async,直接返回了,但是后台还是在执行的
    在这里插入图片描述
    在这里插入图片描述
举报

相关推荐

0 条评论