1、概述
在开发中,我们一般是同步调用,在一些和主线程关联度较低的逻辑,我们可以使用异步调用。例如说:记录用户访问日志到数据库。
在 Spring Framework 的 Spring Task 模块,提供了 @Async
注解,可以添加在方法上,自动实现该方法的异步调用。
2、简单使用
2.1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
2.2、启用异步功能
@EnableAsync
public class Springboot02Application {
public static void main(String[] args) {
SpringApplication.run(Springboot02Application.class, args);
}
}
2.3、DemoService
@Slf4j
@Service
public class DemoService {
@Async //异步
public Integer servic1() {
log.info("[servic1]");
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 1;
}
@Async
public Integer servic2() {
log.info("[servic2]");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 2;
}
}
@Async
注解
2.4、单元测试
@Slf4j
@SpringBootTest(classes = Springboot02Application.class)
class DemoServiceTest {
@Autowired
private DemoService demoService;
@Test
public void task01() {
long now = System.currentTimeMillis();
log.info("[task01][开始执行]");
demoService.servic1();
demoService.servic2();
log.info("[task01][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now);
}
}
2.5、等待异步调用结果
改造DemoService
@Slf4j
@Service
public class DemoService {
public Integer servic1() {
log.info("[servic1]");
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 1;
}
@Async
public Future<Integer> servic1AsyncWithFuture() {
return AsyncResult.forValue(this.servic1());
}
public Integer servic2() {
log.info("[servic2]");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 2;
}
@Async
public Future<Integer> servic2AsyncWithFuture() {
return AsyncResult.forValue(this.servic2());
}
}
//DemoServiceTest.java
@Test
public void task02() throws ExecutionException, InterruptedException{
long now = System.currentTimeMillis();
log.info("[task01][开始执行]");
Future<Integer> integerFuture = demoService.servic1AsyncWithFuture();
Future<Integer> integerFuture1 = demoService.servic2AsyncWithFuture();
integerFuture.get();
integerFuture1.get();
log.info("[task01][结束执行,消耗时长 {} 毫秒]", System.currentTimeMillis() - now);
}
2.6、配置ThreadPoolTaskExecutor
手动配置,如:
@Configuration
public class ExecutorConfig
{
@Bean
public TaskExecutor taskExecutor()
{
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(5);
// 设置最大线程数
executor.setMaxPoolSize(10);
// 设置队列容量
executor.setQueueCapacity(20);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("pool-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
@Async("taskExecutor")
public void test1(){
log.info("hello world")
}
自动配置
在 application.yml
中,添加 Spring Task的配置,如下:
spring:
task:
# Spring 执行器配置,对应 TaskExecutionProperties 配置类。对于 Spring 异步任务,会使用该执行器。
execution:
thread-name-prefix: task- # 线程池的线程名的前缀。默认为 task- ,建议根据自己应用来设置
pool: # 线程池相关
core-size: 8 # 核心线程数,线程池创建时候初始化的线程数。默认为 8 。
max-size: 20 # 最大线程数,线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE
keep-alive: 60s # 允许线程的空闲时间,当超过了核心线程之外的线程,在空闲时间到达之后会被销毁。默认为 60 秒
queue-capacity: 200 # 缓冲队列大小,用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。
allow-core-thread-timeout: true # 是否允许核心线程超时,即开启线程池的动态增长和缩小。默认为 true 。
shutdown:
await-termination: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
await-termination-period: 60 # 等待任务完成的最大时长,单位为秒。默认为 0 ,根据自己应用来设置
- 在
spring.task.execution
配置项,Spring Task 调度任务的配置,对应 TaskExecutionProperties]配置类。 - Spring Boot TaskExecutionAutoConfiguration自动化配置类,实现 Spring Task 的自动配置,创建 ThreadPoolTaskExecutor基于线程池的任务执行器。本质上,ThreadPoolTaskExecutor 是基于 ThreadPoolExecutor 的封装,主要增加提交任务,返回 ListenableFuture 对象的功能。