Spring Boot实现异步调用(多线程)
Spring Boot实现异步调用(多线程)
制作人:全心全意
Spring Boot实现异步调用(多线程)
启动加上@EnableAsync,需要执行的异步方法上加上@Async
@Async实际上就是多线程封装的
使用场景例如,发送短信验证码
异步线程执行方法有可能会非常消耗CPU资源,所以大的项目建议使用MQ异步实现
失效问题:如果异步注解写成当前自己类,有可能aop会失效,无法拦截注解,最终导致异步失效,需要经过代理类调用接口,所以需要将异步的代码单独抽取成一个类调用接口。
多线程使用示例(不使用注解),不建议使用
package com.zq.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import lombok.extern.slf4j.Slf4j; @RestController @Slf4j public class Myxiancheng { @RequestMapping("/d1xiancheng") public String d1xiancheng() { log.info("======1======="); // 发送短信 // 单线程 // sms(); // 多线程 new Thread(new Runnable() { @Override public void run() { sms(); } }).start(); log.info("======4======="); return "我是返回结果"; } public String sms() { log.info("======2======="); try { log.info("正在发送短信======"); Thread.sleep(3000); } catch (Exception e) { } log.info("======3======="); return "短信发送完成"; } }
多线程使用示例(使用注解)
失效问题:如果异步注解写成当前自己类,有可能aop会失效,无法拦截注解,最终导致异步失效,需要经过代理类调用接口,所以需要将异步的代码单独抽取成一个类调用接口(不要在一个包中)。异步方法和controller不要在一个包中
线程方法类
package com.zq.async; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class MyAsync { @Async public String sms() { log.info("======2======="); try { log.info("正在发送短信======"); Thread.sleep(3000); } catch (Exception e) { } log.info("======3======="); return "短信发送完成"; } }
调用controller类
package com.zq.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.zq.async.MyAsync; import lombok.extern.slf4j.Slf4j; @RestController @Slf4j public class Myxiancheng { @Autowired private MyAsync myAsync; @RequestMapping("/d1xiancheng") public String d1xiancheng() { log.info("======1======="); // 发送短信 myAsync.sms(); log.info("======4======="); return "我是返回结果"; } }
启动类
package com.zq; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync // 开启异步注解 @MapperScan("com.zq.mapper") // 默认不会扫描mapper,需要此注解指定 public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
整合线程池
不建议频繁创建线程,频繁的创建线程效率非常低,所以使用线程池
创建线程池配置类
package com.zq.config; import java.util.concurrent.ThreadPoolExecutor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @EnableAsync public class ThreadPoolConfig { /** * 每秒需要多少个线程处理 * tasks/(1/taskcost) */ private int corePoolSize = 3; /** * 线程池维护线程的最大数量 * (max(tasks)- queueCapacity)/(1/taskcost) */ private int maxPoolSize = 3; /** * 缓存队列 * (coreSizePool/taskcost)*responsetime */ private int queueCapacity = 10; /** * 允许的空闲时间 * 默认为60 */ private int keepAlive = 100; @Bean public TaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //设置核心线程数 executor.setCorePoolSize(corePoolSize); //设置最大线程数 executor.setMaxPoolSize(maxPoolSize); //设置队列容量 executor.setQueueCapacity(queueCapacity); //设置允许的空闲时间(秒) //executor.setKeepAliveSeconds(keepAlive); //设置默认的线程名称 executor.setThreadNamePrefix("thread-"); //设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.setWaitForTasksToCompleteOnShutdown(true); return executor; } }
使用线程池
package com.zq.async; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class MyAsync { @Async("taskExecutor") //使用taskExecutor线程池 public String sms() { log.info("======2======="); try { log.info("正在发送短信======"); Thread.sleep(3000); } catch (Exception e) { } log.info("======3======="); return "短信发送完成"; } }