Home Tags Posts tagged with "异步"

异步

多线程下的异步处理可以加快任务处理的速度,提升用户体验,特此记录

 

首先编写线程池配置类,配置Springboot线程池

@Configuration//声明为配置文件并装配到spring
@ComponentScan("com.yang.service")
@EnableAsync//开启异步任务支支持
public class ThreadConfig implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        taskExecutor.setCorePoolSize(5);
        //配置最大线程数
        taskExecutor.setMaxPoolSize(10);
        //空闲的线程多久时间后被销毁
        taskExecutor.setKeepAliveSeconds(25);
        //执行初始化
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
    }
}
定义异步任务实现类CustomMultiThreadingService 
在该类中创建两个异步方法进行邮件发送
@Transactional(rollbackFor = Exception.class)
@Service
public class CustomMultiThreadingService {


    @Autowired
    JavaMailSenderImpl javaMailSender;

    @Autowired
    JavaMailSenderImpl javaMailSender2;
    private Logger logger = LoggerFactory.getLogger(CustomMultiThreadingService.class);
    /**
     * @Description:通过@Async注解表明该方法是一个异步方法,
     * 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
     * @Title: executeAysncTask1
     */
    //在特定时间执行这行代码 Timer
    @Async
    public void executeAysncTask1(Integer i){
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo("1606598203@qq.com");
        message.setSubject("你好");
        message.setText("9973");
        message.setFrom("2690706243@qq.com");
        javaMailSender.send(message);
        logger.info("CustomMultiThreadingService ==> executeAysncTask1 method: 执行异步任务{} ", i);
    }

    /**
     * @Description:通过@Async注解表明该方法是一个异步方法,
     * 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
     * @Title: executeAsyncTask2
     */
    @Async
    public void executeAsyncTask2(Integer i){
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo("2690706243@qq.com");
        message.setSubject("你好");
        message.setText("9973");
        message.setFrom("2690706243@qq.com");
        javaMailSender.send(message);
        logger.info("CustomMultiThreadingService ==> executeAsyncTask2 method: 执行异步任务{} ", i);
    }
}

定义CustomMultiThreadingController类
CustomMultiThreadingController类调用多线程任务CustomMultiThreadingService类
@Transactional(rollbackFor = Exception.class)
@Controller
public class CustomMultiThreadingController {
    @Autowired
    private CustomMultiThreadingService customMultiThreadingService;

    TransactionManager transactionManager;
    @ResponseBody
    @RequestMapping("/dotask")
    public String doTask() {
        for (int i=0;i<1;i++){
                customMultiThreadingService.executeAysncTask1(i);
                customMultiThreadingService.executeAsyncTask2(i);

//            customMultiThreadingService.Tohello();
        }

        return "success";
    }
}

启动类 
//开启定时功能的注解
@SpringBootApplication
@EnableScheduling
public class Application {

   public static void main(String[] args) throws InterruptedException {
      SpringApplication.run(Application.class, args);
      
   }
}
测试:访问http://127.0.0.1:8080/dotask端口

成功发送,异步发送邮件模块实现!!!

场景:现在需要上传一个Excel表格,数据量几万条,而且,上传解析后还需要进行计算,然后插入数据库。

分析:上传和解析,都很简单,但是,这里如果使用同步方式,那么:上传–>解析–>运算–>插入数据库;这个过程,前台的页面都是等待状态的,用户会以为页面卡死了。所以,这里需要做异步处理:

1.上传–>返回正在解析的标志;

2.解析–>运算–>插入数据库;

此时,当用户上传完文件后,页面立马跳转,解析,运算等工作,继续在后台进行,而用户可以不用等待。

 

首先我们需要异步任务在Springboot是如何开启的,这涉及到三个注解:@EnableAsync、@Async、@ComponentScan

使用多线程,往往是创建Thread,或者是实现Runnable接口,用到线程池时还需要创建Executors

@标志开启使用多线程,@Async加在线程任务的方法上(需要异步执行的方法)

@ComponentScan 的作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中

题外话:

注意:@ComponentScan是组件扫描注解,用来扫描@Controller  @Service  @Repository这类,主要就是定义扫描的路径从中找出标志了需要装配的类到Spring容器中

其次,@MapperScan 是扫描mapper类的注解,就不用在每个mapper类上加@Mapper

 

先来看看如果没有异步执行,程序是怎么运行的吧

@Controller
public class DemoCtroller {

    public void Print(){
        for (int i = 0 ;i < 10;i++){
            System.out.println("第"+i+"个");
        }
    }
}
/**
 * 未开启异步任务
 * 顺序执行
 */
//开启定时功能的注解

@SpringBootApplication
@EnableScheduling
public class Application {

 public static void main(String[] args) {

 ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
 context.getBean(DemoCtroller.class).Print();
 for(int i = 0;i <5 ; i++){
 System.out.println("-----------------");
 }

 }

 


}
运行结果:
程序顺序执行,启动类先从IOC容器中获得DemoController的对象然后执行其Print()方法循环打印出9个数字,等到方法结束后才返回启动类继续往下执行,可见整个流程是同步的;
启动类必须先等DemoController的任务完成才能继续向下

异步执行任务,添加@Async注解到要开启异步的方法上

 


运行结果:

 


注意看此处的启动类上的注解:

启动类上没有@EnableAysnc注解,却也调用了异步方法,这是为什么???

原因是@SpringBootApplication是个很强大的注解,它包含了@EnbaleAsync和@ComponentScan两个注解!

我们往启动类的打印方法加入1s的昏睡,使异步行为更清晰

@SpringBootApplication
@EnableScheduling
public class Application {

   public static void main(String[] args) throws InterruptedException {

      ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
      context.getBean(DemoCtroller.class).Print();
      for(int i = 0;i <10; i++){
         TimeUnit.MICROSECONDS.sleep(1);
         System.out.println("-----------------");
      }

   }

}

运行结果:
可见:SpringBoot在获取到IOC中的DemoController对象后,一方面继续往下执行for循环语句,另一方面获取对象后,执行对象中的Print()方法,Print()方法和主线程是异步执行的!!!