前言
异步编程在企业级开发中是一种常用的手段,常用于解决同步编程下,调用时间过长的问题。流程通常是 “优先返回系统响应结果,将非核心逻辑解耦,放到后续处理”。在Springboot中,提供了天然的异步任务处理方法,在需要用到异步编程时,可以考虑该方法。下面,将详细介绍这种方法。
异步任务的使用方法
① 使用@Async注解开启异步任务之前,需要使用@EnableAsync注解开启异步支持。以下,是一个开启异步支持的Springboot启动类。
@SpringBootApplication(scanBasePackages = {"com.stady.async", "com.stady.config"})
@EnableAsync
@EnableConfigurationProperties({ThreadPoolConfig.class})
public class ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(ApplicationRunner.class, args);
System.out.println("Springboot application run success.");
}
}
② 在Springboot配置文件中,配置线程池属性
spring:
task:
execution:
pool:
allow-core-thread-timeout: true #是否允许核心线程超时
core-size: 3 #核心线程池大小
max-size: 5 #线程池最大大小
queue-capacity: 10 #等待队列容量
keep-alive: 60s #超时时间
spring.task.execution.pool.core-size:核心线程数,默认为8
spring.task.execution.pool.max-size:线程池的最大线程数,默认为int最大值
spring.task.execution.pool.keep-alive:线程终止前允许保持空闲的时间
spring.task.execution.pool.queue-capacity:线程等待队列,默认为int最大值
spring.task.execution.pool.allow-core-thread-timeout:是否允许核心线程超时
③ 接下来,编写异步任务执行类,并在异步方法上,标注@Async注解。
@Component
public class AsyncTask {
@Async
public CompletableFuture asyncMethodA() {
long start = System.currentTimeMillis();
try {
// 模拟异步重试流程
System.out.println("异步任务A开始执行");
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("异步任务A处理异常");
}
long end = System.currentTimeMillis();
long all = end - start;
System.out.println("异步任务A执行完成,耗時:" + all);
return CompletableFuture.completedFuture("ok");
}
}
④ 最后,在测试类中,对该异步任务进行测试。(为了看到异步处理过程,在此我使用join方法阻塞异步处理结果)
@RunWith(SpringRunner.class)
// 这里的ApplicationRunner.class为自己的启动类
@SpringBootTest(classes = ApplicationRunner.class)
public class AsyncTest {
@Resource
private AsyncTask asyncTask;
@Test
public void asyncTaskTest() {
long start = System.currentTimeMillis();
CompletableFuture task1 = asyncTask.asyncMethodA();
CompletableFuture task2 = asyncTask.asyncMethodB();
CompletableFuture task3 = asyncTask.asyncMethodC();
CompletableFuture.allOf(task1, task2, task3).join();
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("all task ok. 耗时:" + time);
}
}
测试结果如下
到此,Springboot中异步任务的简单使用就结束了。接下来,我们来讨论一下如何自定义线程池。
自定义线程池
在实际的企业开发中,业务场景通常多且杂,会有多个业务使用到异步任务。若这些异步任务全都使用一个线程池,通常是效率低下的,较耗资源的,还可能引发严重的问题。所以,正确的做法是,针对不同业务场景编写不同的线程池,来达到资源利用最大化。接下来,将讲述如何自定义一个线程池并使用。 ① 编写一个线程池配置类,如下
@ConfigurationProperties(prefix = "my.task.pool")
public class ThreadPoolConfig {
private int corePoolSize;
private int maxPoolSize;
private int waitQueueSize;
private int keepAliveSeconds;
public int getCorePoolSize() {
return corePoolSize;
}
public void setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
}
public int getMaxPoolSize() {
return maxPoolSize;
}
public void setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
}
public int getWaitQueueSize() {
return waitQueueSize;
}
public void setWaitQueueSize(int waitQueueSize) {
this.waitQueueSize = waitQueueSize;
}
public int getKeepAliveSeconds() {
return keepAliveSeconds;
}
public void setKeepAliveSeconds(int keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
}
}
别忘记启动类上的@EnableConfigurationProperties注解
@EnableConfigurationProperties({ThreadPoolConfig.class})
② 在application.yaml文件中,对其进行配置
my:
task:
pool:
corePoolSize: 1
maxPoolSize: 5
waitQueueSize: 10
keepAliveSeconds: 60
③ 现在,轮到自定义线程池了
@Configuration("myThreadPool")
@EnableAsync
public class MyThreadPool {
@Resource
private ThreadPoolConfig threadPoolConfig;
@Bean
public Executor asyncTaskPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(threadPoolConfig.getCorePoolSize());
executor.setMaxPoolSize(threadPoolConfig.getMaxPoolSize());
executor.setKeepAliveSeconds(threadPoolConfig.getKeepAliveSeconds());
executor.setAllowCoreThreadTimeOut(true);
executor.setQueueCapacity(threadPoolConfig.getWaitQueueSize());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
}
在使用时,需要再@Async注解后,加上线程池的名称,以区分使用不同线程池
@Async("asyncTaskPool")
public CompletableFuture asyncMethodA() {
long start = System.currentTimeMillis();
try {
// 模拟异步重试流程
System.out.println("异步任务A开始执行");
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("异步任务A处理异常");
}
long end = System.currentTimeMillis();
long all = end - start;
System.out.println("异步任务A执行完成,耗時:" + all);
return CompletableFuture.completedFuture("ok");
}
完成自定义线程池的配置之后,uu们可以自己测试一下,看是否生效。
注意
不要在同一个类中调用异步任务
可以将异步任务单独写一个类,之后在其他类中进行使用
不要忘记配置线程池
如果没有配置线程池,很可能会导致OOM,引起严重后果
这篇文章到这里就结束了,有问题和意见的uu可以在评论区留下自己的见解。大家一起探讨一下。0.0