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 "短信發送完成"; } }