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