spring里的@Async注解
结论
spring里直接使用@Async会默认使用SimpleAsyncTaskExecutor,而SimpleAsyncTaskExecutor存在这样的问题:它不会复用已创建的线程而是每次有任务时创建新的线程,严格上说SimpleAsyncTaskExecutor并不是标准的线程池,使用不当会引起OutOfMemoryError问题,虽然可以通过concurrencyLimit设定创建线程的上限,但一般还是不建议使用SimpleAsyncTaskExecutor。
分析
下面从spring源码(5.2.12.RELEASE版本)分析上面的结论。
在spring里,@EnableXXX注解的功能通常是开启某个功能。所以要分析@Async原理得先从@EnableAsync注解入手。
EnableAsync注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
/**
* 监测 @async 注解的方法
*/
Class<? extends Annotation> annotation() default Annotation.class;
/**
* 代理方式设置,默认false 只针对@Async注解的方法
*/
boolean proxyTargetClass() default false;
/**
*指定模式。留意这里默认值,后面AdviceModeImportSelector会用到
*/
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
- @EnableXXX注解通常会和@Import注解配合使用,通过@Import导入配置类,根据配置类自动装配一些Bean,来达到开启某些功能的目的。
- 这里Import的是AsyncConfigurationSelector这个类,AsyncConfigurationSelector及其父类AdviceModeImportSelector的作用是根据@Async里设置的model来选择一个配置(Selects which implementation of {@link AbstractAsyncConfiguration} should be used):
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY://默认为这种模式,会使用ProxyAsyncConfiguration这个配置类
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";
protected String getAdviceModeAttributeName() {
return DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME;
}
/**
* 获取@EnableAsync注解中属性为“mode”的值,根据mode导入ProxyAsyncConfiguration
* 或 AspectJAsyncConfiguration 配置类
*/
@Override
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<?> annType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (attributes == null) {
throw new IllegalArgumentException(String.format(
"@%s is not present on importing class '%s' as expected",
annType.getSimpleName(), importingClassMetadata.getClassName()));
}
AdviceMode adviceMode = attributes.getEnum(getAdviceModeAttributeName());
String[] imports = selectImports(adviceMode);
if (imports == null) {
throw new IllegalArgumentException("Unknown AdviceMode: " + adviceMode);
}
return imports;
}
}
ProxyAsyncConfiguration配置类
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
//实例化一个AsyncAnnotationBeanPostProcessor后置处理器
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
//通过AsyncConfigurer配置好的线程池跟异常处理器设置到这个后置处理器中
bpp.configure(this.executor, this.exceptionHandler);
Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
//判断EnableSync是否有自定义属性
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
return bpp;
}
}
- ProxyAsyncConfiguration主要作用是实例化了一个AsyncAnnotationBeanPostProcessor,这个Bean主要是实现方法或者类上使用@Async注解从而达到异步的效果
AsyncAnnotationBeanPostProcessor类
@Async注解是如何生效的呢?AsyncAnnotationBeanPostProcessor类起到了非常重要的作用。
public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
@Override
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
//创建切面处理
AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
if (this.asyncAnnotationType != null) {
advisor.setAsyncAnnotationType(this.asyncAnnotationType);
}
advisor.setBeanFactory(beanFactory);
this.advisor = advisor;
}
}
AsyncAnnotationAdvisor切面类
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
public AsyncAnnotationAdvisor(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
asyncAnnotationTypes.add(Async.class);
try {
asyncAnnotationTypes.add((Class<? extends Annotation>)
ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
}
catch (ClassNotFoundException ex) {
// If EJB 3.1 API not present, simply ignore.
}
//创建AOP的切面通知
this.advice = buildAdvice(executor, exceptionHandler);
//创建AOP的切点
this.pointcut = buildPointcut(asyncAnnotationTypes);
}
/**
*封装需要织入的切面逻辑
*/
protected Advice buildAdvice(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
//创建切面处理的拦截器
AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
interceptor.configure(executor, exceptionHandler);
return interceptor;
}
/**
* 为目标bean织入当前切面逻辑
*/
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
ComposablePointcut result = null;
//遍历异步注解类型(@Async和自定义的异步注解)并创建切点
for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
//类上的注解
Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
//方法上的注解
Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
if (result == null) {
result = new ComposablePointcut(cpc);
}
else {
result.union(cpc);
}
result = result.union(mpc);
}
return (result != null ? result : Pointcut.TRUE);
}
}
- ProxyAsyncConfiguration本身是一个配置类,它的作用是向容器中添加一个AsyncAnnotationBeanPostProcessor。在AsyncAnnotationBeanPostProcessor这个后置处理器中会生成一个AsyncAnnotationAdvisor对象,而AsyncAnnotationAdvisor对象里会创建切入点与通知,在通知里创建了AnnotationAsyncExecutionInterceptor拦截器,这个拦截器会根据切入点拦截@Async修饰的类与方法并最终实现异步。这里我们重点看下拦截器里invoke方法,它是在AnnotationAsyncExecutionInterceptor的父类AsyncExecutionInterceptor里。
AsyncExecutionInterceptor
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
//获取executor执行器
AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
if (executor == null) {
throw new IllegalStateException(
"No executor specified and no default executor set on AsyncExecutionInterceptor either");
}
//定义一个Callable异步任务
Callable<Object> task = () -> {
try {
//执行被拦截的方法
Object result = invocation.proceed();
if (result instanceof Future) {
return ((Future<?>) result).get();
}
}
catch (ExecutionException ex) {
handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
}
catch (Throwable ex) {
handleError(ex, userDeclaredMethod, invocation.getArguments());
}
return null;
};
//异步执行task,并返回结果
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
//获取异步执行器
@Nullable
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
AsyncTaskExecutor executor = this.executors.get(method);
if (executor == null) {
Executor targetExecutor;
//如果在@Async注解里配置了value,则返回对应的bean
String qualifier = getExecutorQualifier(method);
if (StringUtils.hasLength(qualifier)) {
//获取Async里配置的执行器
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
}
else {//未配置,则使用默认的执行器
targetExecutor = this.defaultExecutor.get();
}
if (targetExecutor == null) {
return null;
}
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
this.executors.put(method, executor);
}
return executor;
}
//获取默认执行器SimpleAsyncTaskExecutor
@Override
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}
//异步执行Task
@Nullable
protected Object doSubmit(Callable<Object> task, AsyncTaskExecutor executor, Class<?> returnType) {
//如果返回值是CompletableFuture类型的(异步方法有返回值)
if (CompletableFuture.class.isAssignableFrom(returnType)) {
//异步执行并返回 CompletableFuture 对象,通过future.get()获取结果
return CompletableFuture.supplyAsync(() -> {
try {
return task.call();
}
catch (Throwable ex) {
throw new CompletionException(ex);
}
}, executor);
}
else if (ListenableFuture.class.isAssignableFrom(returnType)) {
return ((AsyncListenableTaskExecutor) executor).submitListenable(task);
}
else if (Future.class.isAssignableFrom(returnType)) {
return executor.submit(task);
}
else {//没返回值的情况
executor.submit(task);
return null;
}
}
}
这里还要看下父类AsyncExecutionAspectSupport里getDefaultExecutor的方法
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
if (beanFactory != null) {
try {
//查找唯一的一个TaskExecutor类型的bean
return beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
logger.debug("Could not find unique TaskExecutor bean", ex);
try {
//查找名称为“taskExecutor”的Executor类型的Bean
return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
if (logger.isInfoEnabled()) {
logger.info("More than one TaskExecutor bean found within the context, and none is named " +
"'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " +
"as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
}
}
}
catch (NoSuchBeanDefinitionException ex) {
logger.debug("Could not find default TaskExecutor bean", ex);
try {//查找名称为“taskExecutor”的Executor类型的Bean
return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
}
catch (NoSuchBeanDefinitionException ex2) {
logger.info("No task executor bean found for async processing: " +
"no bean of type TaskExecutor and no bean named 'taskExecutor' either");
}
// Giving up -> either using local default executor or none at all...
}
}
return null;//放弃,返回null由子类处理
}
在invoke方法里:
- 根据@Async注解里配置的value获取相应的执行器,如果没有则查找唯一的一个TaskExecutor类型的bean;如果还是没有则查找名称为“taskExecutor”的Executor类型的Bean;如果还是不没有使用默认的执行器SimpleAsyncTaskExecutor
- 定义一个Callable执行被拦截的方法
- 异步执行task,分有返回值与没返回值两种情况
spring里实现的线程池
表列 A | 表列 B |
---|---|
SimpleAsyncTaskExecutor | 每次请求新开线程,没有最大线程数设置.不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程 |
SyncTaskExecutor | 不是异步的线程.同步可以用SyncTaskExecutor,但这个可以说不算一个线程池,因为还在原线程执行。这个类没有实现异步调用,只是一个同步操作。 |
ConcurrentTaskExecutor | Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。 |
SimpleThreadPoolTaskExecutor | 监听Spring’s lifecycle callbacks,并且可以和Quartz的Component兼容.是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。 |
ThreadPoolTaskExecutor | 最常用。要求jdk版本大于等于5。可以在程序而不是xml里修改线程池的配置.其实质是对java.util.concurrent.ThreadPoolExecutor的包装。 |
springboot里使用@Async注解
结论
springboot里直接使用@Async,会使用ThreadPoolTaskExecutor这个线程池,而ThreadPoolTaskExecutor里的核心线程数为8,最大线程数与缓冲队列容量均为Integer.MAX_VALUE。
分析
springboot版本使用的是2.3.x版本。
TaskExecutionAutoConfiguration类
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration{
}
- @ConditionalOnClass(ThreadPoolTaskExecutor.class),是说classpath下有ThreadPoolTaskExecutor这个类才会实例化
- @EnableConfigurationProperties可以看出是会使用TaskExecutionProperties这个配置类
我们再看下TaskExecutionProperties类。
TaskExecutionProperties类
@ConfigurationProperties("spring.task.execution")
public class TaskExecutionProperties {
public static class Pool {
private int queueCapacity = Integer.MAX_VALUE;
private int coreSize = 8;
private int maxSize = Integer.MAX_VALUE;
private boolean allowCoreThreadTimeout = true;
....
}
}
可以很明显的看到ThreadPoolTaskExecutor这个线程池里使用的配置,所以我们还是需要自定义。
springboot使用@Async最佳实践
通过自定义线程池的方式
- 添加配置文件
# 配置核心线程数
async:
executor:
thread:
core_pool_size: 6
# 配置最大线程数
max_pool_size: 12
# 配置队列大小
queue_capacity: 1024
# 配置线程最大空闲时间
keep_alive_seconds: 30
# 配置线程池中的线程的名称前缀
name:
prefix: test-async-
2.初始化线程池
@Configuration
@EnableAsync
public class SelfTaskExecutorConfig1 {
@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
@Value("${async.executor.thread.keep_alive_seconds}")
private int keepAliveSeconds;
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;
@Bean(name = "selfExecutor1")
public Executor selfExecutor1(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//设置核心线程数
executor.setCorePoolSize(corePoolSize);
//设置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//设置缓冲队列大小
executor.setQueueCapacity(queueCapacity);
//设置线程的最大空闲时间,超过了核心线程数之外的线程,在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(keepAliveSeconds);
//设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//线程池关闭时等待任务执行完成
executor.setWaitForTasksToCompleteOnShutdown(true);
//承接上一参数,等待时长,如果任务未执行完成达到等待时长后强制关闭
executor.setAwaitTerminationSeconds(60);
//设置线程名前缀
executor.setThreadNamePrefix(namePrefix);
// 线程池初始化
executor.initialize();
return executor;
}
@Bean(name = "selfExecutor2")
public Executor selfExecutor2(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//设置核心线程数
executor.setCorePoolSize(corePoolSize);
//设置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//设置缓冲队列大小
executor.setQueueCapacity(queueCapacity);
//设置线程的最大空闲时间,超过了核心线程数之外的线程,在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(keepAliveSeconds);
//设置拒绝策略,也可以实现RejectedExecutionHandler进行自定义
executor.setRejectedExecutionHandler(new SelfRejectHandle());
//线程池关闭时等待任务执行完成
executor.setWaitForTasksToCompleteOnShutdown(true);
//承接上一参数,等待时长,如果任务未执行完成达到等待时长后强制关闭
executor.setAwaitTerminationSeconds(60);
//设置线程名前缀
executor.setThreadNamePrefix(namePrefix);
// 线程池初始化
executor.initialize();
return executor;
}
/**
* 自定义拒绝策略
*/
class SelfRejectHandle implements RejectedExecutionHandler{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
}
}
- 使用
@RestController
public class ThreadPoolController {
@Autowired
private ThreadExecutorService threadExecutorService;
@RequestMapping("/test")
public String test() {
for(int i=0;i<8;i++) {
threadExecutorService.test();
}
return "test";
}
}
@Service
public class ThreadExecutorService {
@Async("selfExecutor2")//指定使用哪个线程池
public void test(){
try {
TimeUnit.SECONDS.sleep(1);
}catch (InterruptedException e){
//不处理中断异常
}
System.out.println(Thread.currentThread().getName()+" thread pool test");
}
}
使用注意事项
- 异步方法上能使用static修饰,与AOP有关
- 异步方法不能在本类里调用,同样与AOP有关(如果一定要在本类里调用,可通过获取spring上下文后getBean,然后再调用)
- 异步方法返回值必须是void或Future
- 出现异常时,有返回值与无返回值都可以在线程池里捕获异常,另外如果是有返回值,那在future.get()的时候也可以捕获异常
@Async与事务
- Async与Transactional同时存在一个方法上,事务生效
- 主线程里有事务,子线程里的异常不会影响主线程,事务不生效
- 异步线程里去调用有事务的方法,事务方法如果出现异常,事务生效
- 异步线程里去调用有事务的方法,异步线程里出现异常,事务不会生效
spring中事务在多线程场景下不生效的原因:spring的事务是通过数据库连接来实现,而数据库连接spring是放在threadLocal里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的,即是属于不同事务
示例
@Service
public class UserService {
@Autowired
private ApplicationContext applicationContext;
@Resource
private UserDao userDao;
/**
* 这样使用事务是生效的,会回滚
* @param exFlg
*/
@Async("selfExecutor2")
@Transactional
public void asyncTaskForTransaction(Boolean exFlg) {
User user = new User();
user.setUserName("tian");
user.setPassword("98");
user.setSex("1");
userDao.addUser(user);
if (exFlg) {
throw new RuntimeException("模拟异常");
}
}
/**
* 子线程抛出了异常,但不会影响到主线程,所以主线程里的事务不会回滚
* @param exFlg
*/
@Transactional
public void asyncTaskForTransaction2(Boolean exFlg) {
User user = new User();
user.setUserName("ti");
user.setPassword("99");
user.setSex("1");
userDao.addUser(user);
applicationContext.getBean(UserService.class).test2(true);
}
@Async("selfExecutor2")
public void test2(Boolean exFlg){
if (exFlg) {
throw new RuntimeException("模拟异常");
}
}
/**
*事务会回滚
*/
@Async("selfExecutor2")
public void asyncTaskForTransaction3(Boolean exFlg) {
applicationContext.getBean(UserService.class).test3(true);
}
@Transactional
public void test3(Boolean exFlg){
User user = new User();
user.setUserName("tiee");
user.setPassword("96");
user.setSex("1");
userDao.addUser(user);
if (exFlg) {
throw new RuntimeException("模拟异常");
}
}
/**
*事务不会回滚
*/
@Async("selfExecutor2")
public void asyncTaskForTransaction4(Boolean exFlg) {
applicationContext.getBean(UserService.class).test4(true);
if (exFlg) {
throw new RuntimeException("模拟异常");
}
}
@Transactional
public void test4(Boolean exFlg){
User user = new User();
user.setUserName("tiee");
user.setPassword("96");
user.setSex("1");
userDao.addUser(user);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。