一、异步任务启动
- 在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理,在处理与第三方系统交互的时候,同步容易造成响应迟缓的情况
- 在Spring 3.x之后,就已经内置了**@Async**来完美解决这个问题,@Async为异步执行注解
- 异步执行为直接返回null,或者方法不需要返回值
- 所以需要注意异步方法的返回值需要能接收null,推荐无返回值,因为返回的也是null
- 两个重要注解
- @EnableAysnc:启动类上开启异步模式
- @Aysnc:需要异步处理的方法
- @Aysnc异步方法
- 注意不能自调用,否则注解失效
- 创建异步执行方法
import com.codecoord.util.PrintUtil;
import java.time.LocalDateTime;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class AsyncHandler {
@Async
public Long handler(long batch) {
PrintUtil.print("任务任务开始执行....批次号:" + batch);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
PrintUtil.print("任务任务执行结束....批次号:" + batch + ",完成时间:" + LocalDateTime.now());
return batch;
}
}
- 创建异步执行controller
import com.codecoord.springboot.practice.async.handler.AsyncHandler;
import java.time.LocalDateTime;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncController {
@Resource
private AsyncHandler asyncHandler;
@RequestMapping("/async")
public Object async() {
long batch = System.currentTimeMillis();
return batch + " : " + asyncHandler.handler(batch) + ",完成时间:" + LocalDateTime.now();
}
}
- 启动类开启异步功能
import com.codecoord.InitialBootApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@InitialBootApplication
@EnableAsync
public class SpringbootAsyncApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAsyncApplication.class, args);
}
}
- 访问接口,可以看到有如下处理
接口返回时间
1619428692595 : null,完成时间:2021-04-26T17:18:12.603
方法处理时间
task-1 - 任务任务开始执行....批次号:1619428692595
task-1 - 任务任务执行结束....批次号:1619428692595,完成时间:2021-04-26T17:18:15.614
- 从执行结果可以看到,接口对于异步方法返回的是false,没有等待返回值;方法处理时间和接口返回是独立的,也就是收到请求之后就返回;方法执行的没有使用main线程,说明是单独开启线程推送的
二、异步线程池配置
- @Aysnc默认使用默认的线程处理,如果需要自定义则需要自定义配置
- 新建线程配置类,用于接收配置文件配置参数
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "async.task.pool")
public class AsyncTaskPoolProperties {
/**
* 核心线程池
*/
private int corePoolSize = 5;
/**
* 最大线程池
*/
private int maxPoolSize = 50;
/**
* 线程空闲时间
*/
private int keepAliveSeconds = 60;
/**
* 队列长度
*/
private int queueCapacity = 10000;
/**
* 线程名称前缀
*/
private String threadNamePrefix = "TX-AsyncTask-";
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 getKeepAliveSeconds() {
return keepAliveSeconds;
}
public void setKeepAliveSeconds(int keepAliveSeconds) {
this.keepAliveSeconds = keepAliveSeconds;
}
public int getQueueCapacity() {
return queueCapacity;
}
public void setQueueCapacity(int queueCapacity) {
this.queueCapacity = queueCapacity;
}
public String getThreadNamePrefix() {
return threadNamePrefix;
}
public void setThreadNamePrefix(String threadNamePrefix) {
this.threadNamePrefix = threadNamePrefix;
}
}
- 实现AsyncConfigurer接口,实现该接口后将会作为默认线程提供;也可以继承AsyncConfigurerSupport
- getAsyncExecutor:配置线程执行器
- getAsyncUncaughtExceptionHandler:异常处理方法
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import javax.annotation.Resource;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* 异动线程池配置
*/
@Configuration
public class AsyncTaskPoolConfig implements AsyncConfigurer {
@Resource
private AsyncTaskPoolProperties asyncTaskPoolProperties;
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(asyncTaskPoolProperties.getCorePoolSize());
executor.setMaxPoolSize(asyncTaskPoolProperties.getMaxPoolSize());
executor.setKeepAliveSeconds(asyncTaskPoolProperties.getKeepAliveSeconds());
executor.setQueueCapacity(asyncTaskPoolProperties.getQueueCapacity());
executor.setThreadNamePrefix(asyncTaskPoolProperties.getThreadNamePrefix());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setAllowCoreThreadTimeOut(true);
// 线程池初始化
executor.initialize();
return executor;
}
/**
* 异常处理
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
System.out.println("throwable = " + throwable + ", method = " + method + ", objects = " + Arrays
.deepToString(objects));
}
};
}
}
- 可以在配置文件中执行配置,也可以使用默认
async:
task:
pool:
corePoolSize: 10
maxPoolSize: 1000
- 重新启动项目,然后访问,可以看到线程名称将会变成定义的名称
19:22:21.214 - TX-AsyncTask-1 - 任务任务开始执行....批次号:1619436141201
19:22:21.752 - TX-AsyncTask-2 - 任务任务开始执行....批次号:1619436141752
19:22:22.288 - TX-AsyncTask-3 - 任务任务开始执行....批次号:1619436142288
19:22:22.691 - TX-AsyncTask-4 - 任务任务开始执行....批次号:1619436142691
19:22:23.101 - TX-AsyncTask-5 - 任务任务开始执行....批次号:1619436143101
19:22:24.219 - TX-AsyncTask-1 - 任务任务执行结束....批次号:1619436141201,完成时间:2021-04-26T19:22:24.219
19:22:24.759 - TX-AsyncTask-2 - 任务任务执行结束....批次号:1619436141752,完成时间:2021-04-26T19:22:24.759
19:22:25.300 - TX-AsyncTask-3 - 任务任务执行结束....批次号:1619436142288,完成时间:2021-04-26T19:22:25.300
19:22:25.705 - TX-AsyncTask-4 - 任务任务执行结束....批次号:1619436142691,完成时间:2021-04-26T19:22:25.705
19:22:26.111 - TX-AsyncTask-5 - 任务任务执行结束....批次号:1619436143101,完成时间:2021-04-26T19:22:26.111