大多数情况下都是通过同步的方式来实现交互处理的
但是在处理与第三方系统交互的时候,经常会响应迟缓
可以使用@Async实现异步调用
1.使用@Async
使用步骤:
使用@EnableAsync开启异步;
定义Spring组件,使用@Component和@Async
(1)添加依赖
org.springframework.boot
spring-boot-starter-web
(2)修改启动类
添加@EnableAsync开启异步
packagecom.abc.xyz;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsyncpublic classXyzApplication {public static voidmain(String[] args) {
SpringApplication.run(XyzApplication.class, args);
}
}
(3)定义Spring 组件
packagecom.abc.xyz;importorg.springframework.scheduling.annotation.Async;importorg.springframework.scheduling.annotation.AsyncResult;importorg.springframework.stereotype.Component;importjava.util.concurrent.Future;
@Componentpublic classMyTask {
@Asyncpublic voidtask1() {long timestamp =System.currentTimeMillis();
Thread thread=Thread.currentThread();
System.out.println("task1任务开始, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());try{
Thread.sleep(3000);
}catch(Exception e) {
e.printStackTrace();
}
timestamp=System.currentTimeMillis();
System.out.println("task1任务结束, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());
}
@Asyncpublic Futuretask2() {long timestamp =System.currentTimeMillis();
Thread thread=Thread.currentThread();
System.out.println("task2任务开始, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());try{
Thread.sleep(3000);
}catch(Exception e) {
e.printStackTrace();
}
timestamp=System.currentTimeMillis();
System.out.println("task2任务结束, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());return new AsyncResult<>("success");
}
}
(4)测试
packagecom.abc.xyz.controller;importcom.abc.xyz.MyTask;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;
@RestControllerpublic classHelloController {
@AutowiredprivateMyTask myTask;
@RequestMapping("/test")publicString test(){
System.out.println("task before");
myTask.task1();
System.out.println("task after");return "OK";
}
}
启动项目
浏览器打开http://localhost:8080/test
输出OK
控制台输出
task before
task after
task1任务开始, timestamp=1576415623066, threadId=51, threadName=task-1
task1任务结束, timestamp=1576415626067, threadId=51, threadName=task-1
从输出看出,任务是异步执行的
2.带有返回值的异步
返回值Future
添加带返回值的task2()
packagecom.abc.xyz;importorg.springframework.scheduling.annotation.Async;importorg.springframework.scheduling.annotation.AsyncResult;importorg.springframework.stereotype.Component;importjava.util.concurrent.Future;
@Componentpublic classMyTask {
@Asyncpublic voidtask1() {long timestamp =System.currentTimeMillis();
Thread thread=Thread.currentThread();
System.out.println("task1任务开始, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());try{
Thread.sleep(3000);
}catch(Exception e) {
e.printStackTrace();
}
timestamp=System.currentTimeMillis();
System.out.println("task1任务结束, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());
}
@Asyncpublic Futuretask2() {long timestamp =System.currentTimeMillis();
Thread thread=Thread.currentThread();
System.out.println("task2任务开始, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());try{
Thread.sleep(3000);
}catch(Exception e) {
e.printStackTrace();
}
timestamp=System.currentTimeMillis();
System.out.println("task2任务结束, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());return new AsyncResult<>("success");
}
}
测试方法
packagecom.abc.xyz.controller;importcom.abc.xyz.MyTask;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.concurrent.Future;
@RestControllerpublic classHelloController {
@AutowiredprivateMyTask myTask;
@RequestMapping("/test")publicString test(){try{long start =System.currentTimeMillis();
System.out.println("task before");
Future rs =myTask.task2();
System.out.println(rs.get());
myTask.task1();
System.out.println("task after");
System.out.println("执行时间"+(System.currentTimeMillis()-start));
}catch(Exception e){
e.printStackTrace();
}return "OK";
}
}
启动项目,控制台输出
task before
task2任务开始, timestamp=1576416889468, threadId=51, threadName=task-1
task2任务结束, timestamp=1576416892469, threadId=51, threadName=task-1
success
task after
执行时间3016
task1任务开始, timestamp=1576416892474, threadId=52, threadName=task-2
task1任务结束, timestamp=1576416895474, threadId=52, threadName=task-2
说明:
@Async注解声明的方法,返回类型要么为void,要么为Future
方法是异步调用的,无法立即返回结果,如果声明为其它返回类型,获取到的是null
声明为Future,则可以获取到任务的执行结果,结果用Future的get()方法获取
3.异常处理
(1)带返回值的异常处理
带Future类型的返回值时,在调Future的get()方法获取任务的执行结果时抛出的异常
修改task2
@Asyncpublic Future task2() throwsException{long timestamp =System.currentTimeMillis();
Thread thread=Thread.currentThread();
System.out.println("task2任务开始, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());try{
Thread.sleep(3000);
}catch(Exception e) {
e.printStackTrace();
}
timestamp=System.currentTimeMillis();
System.out.println("task2任务结束, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());return new AsyncResult<>("success");
}
(2)不带返回值的异常处理
不带返回值的异常无法被调用者捕获,可以实现AsyncUncaughtExceptionHandler来处理
添加 MyAsyncUncaughtExceptionHandler 实现 AsyncUncaughtExceptionHandler
packagecom.tydt.bim.common;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;importjava.lang.reflect.Method;public class MyAsyncUncaughtExceptionHandler implementsAsyncUncaughtExceptionHandler {private final Logger logger = LoggerFactory.getLogger(MyAsyncUncaughtExceptionHandler.class);
@Overridepublic voidhandleUncaughtException(Throwable ex, Method method, Object... params) {
logger.error("Exception occurs in async method:",ex.getMessage());
}
}
添加 AsyncConfig 实现 AsyncConfigurer 并覆盖 AsyncUncaughtExceptionHandler 方法
packagecom.abc.xyz;importorg.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;importorg.springframework.context.annotation.Configuration;importorg.springframework.scheduling.annotation.AsyncConfigurer;
@Configurationpublic class AsyncConfig implementsAsyncConfigurer {
@OverridepublicAsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return newMyAsyncUncaughtExceptionHandler();
}
}
4.线程调度配置
Spring boot 默认配置的线程池是ThreadPoolTaskExecutor
修改配置文件
spring.task.execution.pool.core-size=8spring.task.execution.pool.max-size=16spring.task.execution.pool.queue-capacity=100spring.task.execution.pool.keep-alive=10s
spring.task.execution.pool.core-size # 核心线程数,默认为8
spring.task.execution.pool.max-size # 最大线程数,默认为无限大
spring.task.execution.pool.queue-capacity # 队列容量,默认为无限大
spring.task.execution.pool.keep-alive # 空闲的线程可以保留多少秒,默认为60。如果超过这个时间没有任务调度,则线程会被回收
5.自定义线程调度器
通过实现AsyncConfigurer接口来定义自己的线程调度器
修改 AsyncConfig
packagecom.abc.xyz;importorg.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;importorg.springframework.context.annotation.Configuration;importorg.springframework.scheduling.annotation.AsyncConfigurer;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;importjava.util.concurrent.Executor;
@Configurationpublic class AsyncConfig implementsAsyncConfigurer {
@OverridepublicExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor= newThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("my-executor-");
executor.initialize();returnexecutor;
}
@OverridepublicAsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return newMyAsyncUncaughtExceptionHandler();
}
}
重新启动,控制台输出
task before
2019-12-15 22:00:44.725 INFO 9204 --- [nio-8080-exec-1] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService
task2任务开始, timestamp=1576418444734, threadId=51, threadName=my-executor-1
task2任务结束, timestamp=1576418447736, threadId=51, threadName=my-executor-1
success
task after
执行时间3017
task1任务开始, timestamp=1576418447740, threadId=52, threadName=my-executor-2
task1任务结束, timestamp=1576418450740, threadId=52, threadName=my-executor-2
说明:
线程池的前缀改成了my-executor-
输出o.s.s.concurrent.ThreadPoolTaskExecutor,是因为没有指定异步执行的executor,在第一次执行的时候会进行初始化
6.多线程池
让线程池只做一件事,防止多个共用线程池出线抢占溢出情况
修改 AsyncConfig,添加task1Executor,task2Executor
packagecom.abc.xyz;importorg.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.scheduling.annotation.AsyncConfigurer;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;importjava.util.concurrent.Executor;
@Configurationpublic class AsyncConfig implementsAsyncConfigurer {
@BeanpublicExecutor task1Executor() {
ThreadPoolTaskExecutor executor= newThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("task1-executor-");
executor.initialize();returnexecutor;
}
@BeanpublicExecutor task2Executor() {
ThreadPoolTaskExecutor executor= newThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(3);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("task2-executor-");
executor.initialize();returnexecutor;
}
@OverridepublicExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor= newThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("my-executor-");
executor.initialize();returnexecutor;
}
@OverridepublicAsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return newMyAsyncUncaughtExceptionHandler();
}
}
使用线程池
packagecom.abc.xyz;importorg.springframework.scheduling.annotation.Async;importorg.springframework.scheduling.annotation.AsyncResult;importorg.springframework.stereotype.Component;importjava.util.concurrent.Future;
@Componentpublic classMyTask {
@Async("task1Executor")public voidtask1() {long timestamp =System.currentTimeMillis();
Thread thread=Thread.currentThread();
System.out.println("task1任务开始, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());try{
Thread.sleep(3000);
}catch(Exception e) {
e.printStackTrace();
}
timestamp=System.currentTimeMillis();
System.out.println("task1任务结束, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());
}
@Async("task2Executor")public Future task2() throwsException{long timestamp =System.currentTimeMillis();
Thread thread=Thread.currentThread();
System.out.println("task2任务开始, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());try{
Thread.sleep(3000);
}catch(Exception e) {
e.printStackTrace();
}
timestamp=System.currentTimeMillis();
System.out.println("task2任务结束, timestamp=" + timestamp + ", threadId=" + thread.getId() + ", threadName=" +thread.getName());return new AsyncResult<>("success");
}
}
重新启动,控制台输出
task before
task2任务开始, timestamp=1576419065314, threadId=52, threadName=task2-executor-1
task2任务结束, timestamp=1576419068315, threadId=52, threadName=task2-executor-1
success
task after
执行时间3052
task1任务开始, timestamp=1576419068354, threadId=53, threadName=task1-executor-1
task1任务结束, timestamp=1576419071356, threadId=53, threadName=task1-executor-1