1

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,但这个可以说不算一个线程池,因为还在原线程执行。这个类没有实现异步调用,只是一个同步操作。
ConcurrentTaskExecutorExecutor的适配类,不推荐使用。如果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最佳实践

通过自定义线程池的方式

  1. 添加配置文件
# 配置核心线程数
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) {

        }
    }
  1. 使用
@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与事务

  1. Async与Transactional同时存在一个方法上,事务生效
  2. 主线程里有事务,子线程里的异常不会影响主线程,事务不生效
  3. 异步线程里去调用有事务的方法,事务方法如果出现异常,事务生效
  4. 异步线程里去调用有事务的方法,异步线程里出现异常,事务不会生效

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);
    }
}

步履不停
38 声望13 粉丝

好走的都是下坡路