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