@Async 是 Spring 框架中的一个注解,用于实现方法级别的异步执行。使用 @Async 可以让你的代码在非当前线程中执行,从而提高应用的并发性能。
1、 启用异步支持
在 Spring 应用的主配置类或任何其他配置类上添加 @EnableAsync 注解来开启异步任务的支持
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class Demo2024Application {
public static void main(String[] args) {
SpringApplication.run(Demo2024Application.class, args);
}
}
2、配置异步执行器
默认情况下,Spring 使用一个内置的 SimpleAsyncTaskExecutor。为了更好地控制线程池,你可以自定义一个 ThreadPoolTaskExecutor 并在配置类中声明它。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class AppConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 核心线程数
executor.setMaxPoolSize(10); // 最大线程数
executor.setQueueCapacity(200); // 队列大小
executor.setThreadNamePrefix("MyExecutor-"); // 线程前缀名称
executor.initialize();
return executor;
}
}
3、标注异步方法
在需要异步执行的方法上添加 @Async 注解。这个方法将不会阻塞当前调用线程,而是立即返回,异步执行任务。
@Async
public void testAsyncMethod(){
log.info("执行Service类的异步方法 start .......");
// 执行本Service 类的异步方法 sleep 10s 且每2秒输出一下进度
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(2000);
log.info("执行Service类的异步方法 threadName:{}, progress .......:{}",Thread.currentThread().getName(),i);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("执行Service类的异步方法 end .......");
}
4、注意事项
- a、异步方法不能直接在控制器(Controller)层使用,因为它们通常需要返回一个响应给客户端,这与异步方法的无返回行为不兼容
- b、异步方法不能直接调用同一类中的其他异步方法,否则可能会导致死锁。
通过测试发现,在同一类中调用其它异步方法,打印出来的线程名 都是同一个线程
测试如下 在同一controller中
/**
* 测试本controller 类的异步方法
*/
@GetMapping("/testControllerAsync")
public void testControllerAsync(){
log.info("testControllerAsyncy,调用方法执行打印 开始 .......");
log.info("testControllerAsync method threadName:{}",Thread.currentThread().getName());
// 调用本controller 类的异步方法
testCurrentControllerAsyncMethod();
log.info("testControllerAsync,调用方法执行打印 开始 .......");
}
@Async
public void testCurrentControllerAsyncMethod(){
log.info("执行本controller 类的异步方法 start .......");
// 执行本controller 类的异步方法 sleep 10s 且每2秒输出一下进度
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(2000);
log.info("执行本controller 类的异步方法threadName:{}, progress .......:{}",Thread.currentThread().getName(),i);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("执行本controller 类的异步方法 end .......");
}
调用接口后 输出内容如下 可以看出 调用方法和被调用的异步方法 打印的threadName 是相同的,且调用方法的结束打印是在异步方法执行结束之后执行的,说明异步方法没生效
测试在同一service中
@Slf4j
@RestController
public class AsyncTestController {
@Resource
private AsyncTestService asyncService;
/**
* 调用Service 类的方法,然后service再调用同service类中的异步方法
*/
@GetMapping("/testService")
public void testService(){
log.info("testService controller method 打印开始 .......");
log.info("testService controller method threadName:{}",Thread.currentThread().getName());
// 调用Service 类的方法,然后service再调用同service类中的异步方法
asyncService.testServiceMethod();
log.info("testService controller method 打印开始 .......");
}
}
@Service
@Slf4j
public class AsyncTestService {
@Async
public void testAsyncMethod(){
log.info("执行Service类的异步方法 start .......");
// 执行本Service 类的异步方法 sleep 10s 且每2秒输出一下进度
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(2000);
log.info("执行Service类的异步方法 threadName:{}, progress .......:{}",Thread.currentThread().getName(),i);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("执行Service类的异步方法 end .......");
}
public void testServiceMethod(){
log.info("testServiceMethod method start .......");
log.info("testServiceMethod method threadName:{}",Thread.currentThread().getName());
// 调用Service正常方法,然后正常方法调用本service中的异步方法
testAsyncMethod();
log.info("testServiceMethod method end .......");
}
}
调用接口后输出内容如下 可以看出 调用方法及service中的方法和被调用的异步方法 打印的threadName 是相同的,且调用方法的结束打印和service中调用的方法的打印是在异步方法执行结束之后执行的,说明异步方法没生效
5、 正确的调用 如下,
@Slf4j
@RestController
public class AsyncTestController {
@Resource
private AsyncTestService asyncService;
/**
* 测试service 类的中异步方法
*/
@GetMapping("/testServiceAsync")
public void testServiceAsync(){
log.info("testServiceAsync controller method start .......");
log.info("testServiceAsync controller method threadName:{}",Thread.currentThread().getName());
// 调用Service 类的异步方法
asyncService.testAsyncMethod();
log.info("testServiceAsync controller method end .......");
}
}
@Service
@Slf4j
public class AsyncTestService {
@Async
public void testAsyncMethod(){
log.info("执行Service类的异步方法 start .......");
// 执行本Service 类的异步方法 sleep 10s 且每2秒输出一下进度
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(2000);
log.info("执行Service类的异步方法 threadName:{}, progress .......:{}",Thread.currentThread().getName(),i);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("执行Service类的异步方法 end .......");
}
}
调用后输出如下,可以看出 调用的controller方法 和service中异步方法打印的 线程名称是不相同的,且调用的controller方法的开始打印 及结整打印都是在异步方法打印之前执行的,说明异步方法生效了