@Async 注解
Spring3 开始提供了 @Async 注解,该注解能够标注在办法或者类上,从而能够不便的实现办法的异步调用。调用者在调用异步办法时将立刻返回,办法的理论执行将提交给指定的线程池中的线程执行。
@Async 注意事项:
- @Async 标注在类上时,示意该类的所有办法都是异步办法。
- @Async 注解的办法肯定要通过依赖注入调用(因为要通过 代理对象调用),不能间接通过 this 对象调用,否则不失效。
@Async 应用示例
1、Spring 中启用 @Async
创立一个配置类,并加上 @EnableAsync 注解即可
@Configuration
@EnableAsync
public class SpringAsyncConfig{}
2、应用 @Async
创立异步办法执行类
@Service
public class AsyncService {
// 无返回值的异步办法
@Async
public void noReturnMethod() {String tName = Thread.currentThread().getName();
System.out.println("current thread name :" + tName);
System.out.println("noReturnMethod end");
}
// 有返回值的异步办法
@Async
public Future<String> withReturnMethod() {String tName = Thread.currentThread().getName();
System.out.println("current thread name :" + tName);
return new AsyncResult<>("aaa");
}
}
创立调用异步办法的类
@RestController
@RequestMapping("/api/async/test/")
public class AsyncController {
@Autowired
AsyncService asyncService;
// 无返回值
@GetMapping("/noReturn")
public String noReturn() {asyncService.noReturnMethod();
return "success";
}
// 有返回值
@GetMapping("/withReturn")
public String withReturn() {Future<String> future = asyncService.withReturnMethod();
try {String res = future.get();// 阻塞获取返回值
System.out.println("res =" + res);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}
return "success";
}
}
Spring 定义的线程池类
Spring 曾经定义的线程池类有如下一些:
- SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,默认每次调用都会创立一个新的线程。
- SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只实用于不须要多线程的中央。
- ConcurrentTaskExecutor:Executor 的适配类,不举荐应用。如果 ThreadPoolTaskExecutor 不满足要求时,才用思考应用这个类。
- SimpleThreadPoolTaskExecutor:是 Quartz 的 SimpleThreadPool 的类。线程池同时被 quartz 和非 quartz 应用,才须要应用此类。
- ThreadPoolTaskExecutor:最常应用,举荐。其实质是对 java.util.concurrent.ThreadPoolExecutor 的包装。
配置自定义线程池
异步办法默认的线程池
在 @EnableAsync 注解中有如下正文阐明:
By default, Spring will be searching for an associated thread pool definition:either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context,or an {@link java.util.concurrent.Executor} bean named “taskExecutor” otherwise. Ifneither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}will be used to process async method invocations.
翻译一下就是:
Spring 首先会通过上面两种形式查找作为异步办法的默认线程池:
1、查找惟一的一个 TaskExecutor 类型的 bean
2、或者是一个名称为“taskExecutor”的 Executor 类型的 Bean。
如果下面两种形式都没有查找到,则应用 SimpleAsyncTaskExecutor 作为异步办法的默认线程池
而 SimpleAsyncTaskExecutor 线程池去执行 @Async 标注的异步办法,因为该线程池不会重用线程,所以我的项目中举荐应用自定义的线程池。
配置异步办法默认自定义线程池
配置 @Async 默认的线程池有多种形式:
- 从新实现接口 AsyncConfigurer
- 继承 AsyncConfigurerSupport
- 自定义一个 TaskExecutor 类型的 bean。
- 自定义一个名称为“taskExecutor”的 Executor 类型的 Bean。
上面展现了通过形式 1(从新实现接口 AsyncConfigurer)来配置默认的自定义线程池:
// 配置类实现 AsyncConfigurer 接口的 getAsyncExecutor()办法
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);// 外围池大小
executor.setMaxPoolSize(6);// 最大线程数
executor.setKeepAliveSeconds(60);// 线程闲暇工夫
executor.setQueueCapacity(10);// 队列水平
executor.setThreadNamePrefix("my-executor1-");// 线程前缀名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 配置回绝策略
executor.setAllowCoreThreadTimeOut(true);// 容许销毁外围线程
executor.initialize();
return executor;
}
}
不同异步办法配置不同线程池
有时候不同性能的异步办法须要配置不同的线程池,能够通过在 @Async 上指定线程池的名称来实现
@Configuration
public class ExecutorConfig {@Bean("customExecutor-1")// 自定义线程池 1
public Executor customExecutor1() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);// 外围池大小
executor.setMaxPoolSize(6);// 最大线程数
executor.setKeepAliveSeconds(60);// 线程闲暇工夫
executor.setQueueCapacity(10);// 队列水平
executor.setThreadNamePrefix("customExecutor-1-");// 线程前缀名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 配置回绝策略
executor.setAllowCoreThreadTimeOut(true);// 容许销毁外围线程
executor.initialize();
return executor;
}
@Bean("customExecutor-2")// 自定义线程池 2
public Executor customExecutor2() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);// 外围池大小
executor.setMaxPoolSize(6);// 最大线程数
executor.setKeepAliveSeconds(60);// 线程闲暇工夫
executor.setQueueCapacity(10);// 队列水平
executor.setThreadNamePrefix("customExecutor-2-");// 线程前缀名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 配置回绝策略
executor.setAllowCoreThreadTimeOut(true);// 容许销毁外围线程
executor.initialize();
return executor;
}
}
@Async("customExecutor-1")
public void method1(){}
@Async("customExecutor-2")
public void method2(){}
@Async 的异样解决
当办法是带 Future 返回值的时候,Future.get()办法会抛出异样,所以异样捕捉是没问题的。然而当办法是不带返回值的时候,那么此时主线程就不能捕捉到异样,须要额定的配置来解决异样,能够有上面两种形式。
1、通过 try-catch 解决异样
间接在异步办法中应用 try-catch 来解决抛出的异样。这个办法也能够用于带 Future 返回值的异步办法。
2、通过实现 AsyncUncaughtExceptionHandler 接口
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {// 省略自定义线程池的代码}
// 自定义异样解决
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {System.out.println(method.getName() + "产生异样!异样起因:" + throwable.getMessage());
}
};
}
}
参考资料
- Spring 应用 @Async 注解