简介
本文介绍Spring的@Async的用法。@Async是用来异步执行任务的。
基础代码
正常情况下,执行两个任务是这样的:
Controller
package com.knife.example.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @Api(tags = "测试") @RequestMapping("test") @RestController public class HelloController { @Autowired private HelloService helloService; @ApiOperation("测试1") @PostMapping("test1") public void test() { helloService.task1(); helloService.task2(); } }
Service
package com.knife.example.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Slf4j @Component public class HelloService { public void task1() { log.info("开始任务1"); long start = System.currentTimeMillis(); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } long end = System.currentTimeMillis(); log.info("完成任务1,耗时:" + (end - start) + "毫秒"); } public void task2() { log.info("开始任务2"); long start = System.currentTimeMillis(); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } long end = System.currentTimeMillis(); log.info("完成任务2,耗时:" + (end - start) + "毫秒"); } }
它的结果是:是顺序执行的。
2024-07-07 15:53:00.067 INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService : 开始任务1 2024-07-07 15:53:03.067 INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService : 完成任务1,耗时:3000毫秒 2024-07-07 15:53:03.067 INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService : 开始任务2 2024-07-07 15:53:06.073 INFO 1044 --- [nio-8080-exec-1] c.knife.example.controller.HelloService : 完成任务2,耗时:3006毫秒
使用异步加快速度
假如上边task1和task2是不相关的,那完全可以同时运行,加快速度!下边就用异步来做:
1.启用异步(@EnableAsync)
在启动类上加@EnableAsync
package com.knife.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @EnableAsync @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
2.将方法标记为@Async
@Async也可以放到类上边,表示所有方法都是异步执行。
下边方法上都加了@Async。另外,为了等待任务执行结束,用了CompletableFuture。
package com.knife.example.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import java.util.concurrent.CompletableFuture; @Slf4j @Component public class HelloService { @Async public CompletableFuture<String> task1() { log.info("开始任务1"); long start = System.currentTimeMillis(); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } long end = System.currentTimeMillis(); log.info("完成任务1,耗时:" + (end - start) + "毫秒"); return CompletableFuture.completedFuture("任务1完成"); } @Async public CompletableFuture<String> task2() { log.info("开始任务2"); long start = System.currentTimeMillis(); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } long end = System.currentTimeMillis(); log.info("完成任务2,耗时:" + (end - start) + "毫秒"); return CompletableFuture.completedFuture("任务2完成"); } }
测试:速度变快了,因为使用了两个线程去执行。
2024-07-07 15:59:33.264 INFO 8708 --- [ task-1] c.knife.example.controller.HelloService : 开始任务1 2024-07-07 15:59:33.264 INFO 8708 --- [ task-2] c.knife.example.controller.HelloService : 开始任务2 2024-07-07 15:59:36.270 INFO 8708 --- [ task-2] c.knife.example.controller.HelloService : 完成任务2,耗时:3006毫秒 2024-07-07 15:59:36.270 INFO 8708 --- [ task-1] c.knife.example.controller.HelloService : 完成任务1,耗时:3006毫秒 2024-07-07 15:59:36.270 INFO 8708 --- [nio-8080-exec-1] c.k.example.controller.HelloController : 任务完成,总耗时:3016毫秒
Async的配置
applicaion.yml
spring: task: execution: pool: # 核心线程数。默认是8 core-size: 3 # 最大线程数。默认是整数最大值 max-size: 10 # 队列长度。默认是整数最大值 queue-capacity: 100 # 是否允许核心线程超时。默认是true allow-core-thread-timeout: true # 空闲线程存活时间。默认是60s keep-alive: 60s shutdown: # 退出时等待线程结束。默认是false await-termination: false # 等待线程结束的时间 await-termination-period: 30s # 线程名称前缀。默认是task- thread-name-prefix: task-
自定义线程池
package com.knife.example.config; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; @Configuration public class TaskExecutorConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(3); taskExecutor.setMaxPoolSize(10); taskExecutor.setQueueCapacity(100); taskExecutor.initialize(); return taskExecutor; } }
注意事项
@Async是动态代理实现的,如果调用同一个类里被@Async标记了的方法,需要先注入当前类,再去调用。例如:
package com.knife.example.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.CompletableFuture; @Slf4j @Api(tags = "测试") @RequestMapping("test") @RestController public class HelloController { @Autowired private HelloController helloController; @ApiOperation("测试1") @PostMapping("test1") public void test() { long start = System.currentTimeMillis(); CompletableFuture<String> task1 = helloController.task1(); CompletableFuture<String> task2 = helloController.task2(); CompletableFuture.allOf(task1, task2).join(); long end = System.currentTimeMillis(); log.info("任务完成,总耗时:" + (end - start) + "毫秒"); } @Async public CompletableFuture<String> task1() { log.info("开始任务1"); long start = System.currentTimeMillis(); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } long end = System.currentTimeMillis(); log.info("完成任务1,耗时:" + (end - start) + "毫秒"); return CompletableFuture.completedFuture("任务1完成"); } @Async public CompletableFuture<String> task2() { log.info("开始任务2"); long start = System.currentTimeMillis(); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } long end = System.currentTimeMillis(); log.info("完成任务2,耗时:" + (end - start) + "毫秒"); return CompletableFuture.completedFuture("任务2完成"); } }
请先
!