实际开发过程中,经常遇到业务中需要异步来执行一些业务,SpringBoot给我们提供了简便执行异步业务的注解@Async 如何使用该注解:
- 步骤:
导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.14</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
启动类:
启动类需要添加 @EnableAsync注解 ,以可以使用@Async注解
package com.sun.mycode;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class MyCodeApplication {
public static void main(String[] args) {
SpringApplication.run(MyCodeApplication.class, args);
}
}
Service使用该注解执行
/**
* 异步执行的服务类
*/
public interface AsyncExecuteService {
public <T>void doExecuteDefaultAsync(T t);
}
impl:
@Slf4j
@Service
public class AsyncExecuteServiceImpl implements AsyncExecuteService {
@Override
@Async
public <T> void doExecuteDefaultAsync(T t) {
try {
//避免执行太快看不到效果
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("当前线程Id{} threadName:{}",Thread.currentThread().getId(),Thread.currentThread().getName());
//todo do t Service 执行业务方法
String s = JSONUtil.toJsonStr(t);
System.out.println(s);
}
}
测试类书写:
@SpringBootTest class MyCodeApplicationTests { @Resource private AsyncExecuteService asyncExecuteService; @Test public void test2() { for (int i = 0; i < 100; i++) { asyncExecuteService.doExecuteDefaultAsync(IdUtil.fastSimpleUUID()); } while (true) { } } }
测试结果:
可见会开启不同线程来执行具体方法:
此处需要注意:如果没有在@Async
注解中指定线程池,就会使用默认的线程池SimpleAsyncTaskExecutor
。该线程池默认来一个任务创建一个线程,在压测情况下,会有大量请求,这时就会不断创建大量线程,极有可能出现OOM的问题。
在实际开发中我们需要自定义线程池来进行控制创建的线程:
自定义线程池配置:
@Configuration
@ConfigurationProperties(prefix = "async.executor.thread")
@Data
public class ExecutorConfig implements AsyncConfigurer {
private Integer corePoolSize;
private Integer maxPoolSize;
private Integer queueCapacity;
private String prefix;
@Override
@Bean("asyncExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中线程的名称前缀
executor.setThreadNamePrefix(prefix);
//策略ThreadPoolExecutor.AbortPolicy():丢弃任务并抛出RejectedExecutionException异常。 默认策略
//ThreadPoolExecutor.DiscardPolicy():也是丢弃任务,但是不抛出异常。
//ThreadPoolExecutor.DiscardOldestPolicy():丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
//ThreadPoolExecutor.CallerRunsPolicy():由调用线程处理该任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
application.yml配置文件:
async:
executor:
thread:
corePoolSize: 5
maxPoolSize: 5
queueCapacity: 999
prefix: async
service书写:
public interface AsyncExecuteService {
public <T>void doExecuteAsync(T t);
}
impl:
@Slf4j
@Service
public class AsyncExecuteServiceImpl implements AsyncExecuteService {
@Override
@Async("asyncExecutor")
public <T> void doExecuteAsync(T t) {
try {
//避免执行太快看不到效果
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("当前线程Id{} threadName:{}",Thread.currentThread().getId(),Thread.currentThread().getName());
//todo do t Service 执行业务方法
String s = JSONUtil.toJsonStr(t);
System.out.println(s);
}
}
测试类书写:
@SpringBootTest
class MyCodeApplicationTests {
@Resource
private AsyncExecuteService asyncExecuteService;
@Test
public void test1() {
for (int i = 0; i < 50; i++) {
asyncExecuteService.doExecuteAsync(IdUtil.fastSimpleUUID());
}
while (true) {
}
}
}
测试结果:
可见是由我们自定义的线程池创建的线程