@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;@Configurationpublic class AppConfig implements AsyncConfigurer {@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5); // 核心线程数executor.setMaxPoolSize(10); // 最大线程数executor.setQueueCapacity(200); // 队列大小executor.setThreadNamePrefix("MyExecutor-"); // 线程前缀名称executor.initialize();return executor;}}
3、标注异步方法
在需要异步执行的方法上添加 @Async 注解。这个方法将不会阻塞当前调用线程,而是立即返回,异步执行任务。
@Asyncpublic 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,调用方法执行打印 开始 .......");}@Asyncpublic 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 {@Resourceprivate 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 {@Asyncpublic 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 {@Resourceprivate 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 {@Asyncpublic 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方法的开始打印 及结整打印都是在异步方法打印之前执行的,说明异步方法生效了