SpringBoot中@EnableAsync和@Async简介

1,271 阅读15分钟

参考资料:

blog.csdn.net/qq_38796327…

blog.csdn.net/supzhili/ar…

blog.csdn.net/weixin_4385…

blog.csdn.net/weixin_4385…

在项目中, 偶尔需要使用异步的方式去执行任务.所以,我们可以引入多线程的使用,SpringBoot中支持多线程,使用@EnableAsync注解就可以使用多线程了,@Async放在需要异步执行的方法上,非常简单方便的使用多线程去完成任务.

0 同步和异步的执行说明

1 案例一:

@RestController
public class UserController {
 
    @Autowired
    private UserService userService;

    @GetMapping("/test")
    public void test() {
        System.out.println("获取主线程名称:" + Thread.currentThread().getName());
        userService.serviceTest();
        System.out.println("执行成功,返回结果");
    }
}
@Service
public class UserService {
 
    public void serviceTest() {
            // 这里执行实际的业务逻辑,在这里我们就是用一个简单的遍历来模拟
            Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach( t -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("获取number为:" + t) ;
            });
    }
}

访问: http://localhost:8080/test 返回结果如下:

获取主线程名称:http-nio-8080-exec-3
获取number为:1
获取number为:2
获取number为:3
获取number为:4
获取number为:5
获取number为:6
获取number为:7
获取number为:8
获取number为:9
获取number为:10
执行成功,返回结果

从结果来看.同步操作,单线程执行任务后返回结果.

2 案例二:

@RestController
public class UserController {
 
    @Autowired
    private UserService userService;

@GetMapping("/test2")
    public void test2() {
        System.out.println("获取主线程名称:" + Thread.currentThread().getName());
        
         ThreadPoolExecutor executor = new ThreadPoolExecutor(1,5,50000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
        // 执行业务逻辑方法
        executor.execute(new Runnable() {
            @Override
            public void run() {
                userService.serviceTest();
            }
            });
        System.out.println("执行完成,向用户响应成功信息");
    }
     
}

访问: http://localhost:8080/test2 返回结果如下:

获取主线程名称:http-nio-8080-exec-1
执行完成,向用户响应成功信息
获取number为:1
获取number为:2
获取number为:3
获取number为:4
获取number为:5
获取number为:6
获取number为:7
获取number为:8
获取number为:9
获取number为:10

从结果来看.异步操作,多线程执行任务.先返回结果,后执行任务.

3 案例三:

@RestController
public class UserController {
 
    @Autowired
    private UserService userService;

    @GetMapping("/test3")
    public void test3() {
        System.out.println("获取主线程名称:" + Thread.currentThread().getName());
        userService.serviceTest2();
        System.out.println("执行成功,返回结果");
    }
}
@Service
@EnableAsync
public class UserService {
/**
 * 采用异步执行
 */
@Async
public void serviceTest2() {
        // 这里执行实际的业务逻辑,在这里我们就是用一个简单的遍历来模拟
        Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach( t -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("获取number为:" + t) ;
        });
}

访问: http://localhost:8080/test3 返回结果如下:

获取主线程名称:http-nio-8080-exec-1
执行成功,返回结果
获取number为:1
获取number为:2
获取number为:3
获取number为:4
获取number为:5
获取number为:6
获取number为:7
获取number为:8
获取number为:9
获取number为:10

从结果来看.异步操作,多线程执行任务.先返回结果,后执行任务.执行效果同案例二.但案例三大大地简化了代码的编写,提高了编程的效率.

1 @EnableAsync和@Async分析

1@EnableAsync源码说明

/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;

/**
 * Enables Spring's asynchronous method execution capability, similar to functionality
 * found in Spring's {@code <task:*>} XML namespace.
 *
 * <p>To be used together with @{@link Configuration Configuration} classes as follows,
 * enabling annotation-driven async processing for an entire Spring application context:
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableAsync
 * public class AppConfig {
 *
 * }</pre>
 *
 * {@code MyAsyncBean} is a user-defined type with one or more methods annotated with
 * either Spring's {@code @Async} annotation, the EJB 3.1 {@code @javax.ejb.Asynchronous}
 * annotation, or any custom annotation specified via the {@link #annotation} attribute.
 * The aspect is added transparently for any registered bean, for instance via this
 * configuration:
 *
 * <pre class="code">
 * &#064;Configuration
 * public class AnotherAppConfig {
 *
 *     &#064;Bean
 *     public MyAsyncBean asyncBean() {
 *         return new MyAsyncBean();
 *     }
 * }</pre>
 *
 * <p>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. If
 * neither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}
 * will be used to process async method invocations. Besides, annotated methods having a
 * {@code void} return type cannot transmit any exception back to the caller. By default,
 * such uncaught exceptions are only logged.
 *
 * <p>To customize all this, implement {@link AsyncConfigurer} and provide:
 * <ul>
 * <li>your own {@link java.util.concurrent.Executor Executor} through the
 * {@link AsyncConfigurer#getAsyncExecutor getAsyncExecutor()} method, and</li>
 * <li>your own {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler
 * AsyncUncaughtExceptionHandler} through the {@link AsyncConfigurer#getAsyncUncaughtExceptionHandler
 * getAsyncUncaughtExceptionHandler()}
 * method.</li>
 * </ul>
 *
 * <p><b>NOTE: {@link AsyncConfigurer} configuration classes get initialized early
 * in the application context bootstrap. If you need any dependencies on other beans
 * there, make sure to declare them 'lazy' as far as possible in order to let them
 * go through other post-processors as well.</b>
 *
 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableAsync
 * public class AppConfig implements AsyncConfigurer {
 *
 *     &#064;Override
 *     public Executor getAsyncExecutor() {
 *         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 *         executor.setCorePoolSize(7);
 *         executor.setMaxPoolSize(42);
 *         executor.setQueueCapacity(11);
 *         executor.setThreadNamePrefix("MyExecutor-");
 *         executor.initialize();
 *         return executor;
 *     }
 *
 *     &#064;Override
 *     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
 *         return new MyAsyncUncaughtExceptionHandler();
 *     }
 * }</pre>
 *
 * <p>If only one item needs to be customized, {@code null} can be returned to
 * keep the default settings. Consider also extending from {@link AsyncConfigurerSupport}
 * when possible.
 *
 * <p>Note: In the above example the {@code ThreadPoolTaskExecutor} is not a fully managed
 * Spring bean. Add the {@code @Bean} annotation to the {@code getAsyncExecutor()} method
 * if you want a fully managed bean. In such circumstances it is no longer necessary to
 * manually call the {@code executor.initialize()} method as this will be invoked
 * automatically when the bean is initialized.
 *
 * <p>For reference, the example above can be compared to the following Spring XML
 * configuration:
 *
 * <pre class="code">
 * &lt;beans&gt;
 *
 *     &lt;task:annotation-driven executor="myExecutor" exception-handler="exceptionHandler"/&gt;
 *
 *     &lt;task:executor id="myExecutor" pool-size="7-42" queue-capacity="11"/&gt;
 *
 *     &lt;bean id="asyncBean" class="com.foo.MyAsyncBean"/&gt;
 *
 *     &lt;bean id="exceptionHandler" class="com.foo.MyAsyncUncaughtExceptionHandler"/&gt;
 *
 * &lt;/beans&gt;
 * </pre>
 *
 * The above XML-based and JavaConfig-based examples are equivalent except for the
 * setting of the <em>thread name prefix</em> of the {@code Executor}; this is because
 * the {@code <task:executor>} element does not expose such an attribute. This
 * demonstrates how the JavaConfig-based approach allows for maximum configurability
 * through direct access to actual componentry.
 *
 * <p>The {@link #mode} attribute controls how advice is applied: If the mode is
 * {@link AdviceMode#PROXY} (the default), then the other attributes control the behavior
 * of the proxying. Please note that proxy mode allows for interception of calls through
 * the proxy only; local calls within the same class cannot get intercepted that way.
 *
 * <p>Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the
 * value of the {@link #proxyTargetClass} attribute will be ignored. Note also that in
 * this case the {@code spring-aspects} module JAR must be present on the classpath, with
 * compile-time weaving or load-time weaving applying the aspect to the affected classes.
 * There is no proxy involved in such a scenario; local calls will be intercepted as well.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @author Sam Brannen
 * @since 3.1
 * @see Async
 * @see AsyncConfigurer
 * @see AsyncConfigurationSelector
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {

	/**
	 * Indicate the 'async' annotation type to be detected at either class
	 * or method level.
	 * <p>By default, both Spring's @{@link Async} annotation and the EJB 3.1
	 * {@code @javax.ejb.Asynchronous} annotation will be detected.
	 * <p>This attribute exists so that developers can provide their own
	 * custom annotation type to indicate that a method (or all methods of
	 * a given class) should be invoked asynchronously.
	 */
	Class<? extends Annotation> annotation() default Annotation.class;

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies.
	 * <p><strong>Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}</strong>.
	 * <p>The default is {@code false}.
	 * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
	 * Spring-managed beans requiring proxying, not just those marked with {@code @Async}.
	 * For example, other beans marked with Spring's {@code @Transactional} annotation
	 * will be upgraded to subclass proxying at the same time. This approach has no
	 * negative impact in practice unless one is explicitly expecting one type of proxy
	 * vs. another &mdash; for example, in tests.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate how async advice should be applied.
	 * <p><b>The default is {@link AdviceMode#PROXY}.</b>
	 * Please note that proxy mode allows for interception of calls through the proxy
	 * only. Local calls within the same class cannot get intercepted that way; an
	 * {@link Async} annotation on such a method within a local call will be ignored
	 * since Spring's interceptor does not even kick in for such a runtime scenario.
	 * For a more advanced mode of interception, consider switching this to
	 * {@link AdviceMode#ASPECTJ}.
	 */
	AdviceMode mode() default AdviceMode.PROXY;

	/**
	 * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor}
	 * should be applied.
	 * <p>The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run
	 * after all other post-processors, so that it can add an advisor to
	 * existing proxies rather than double-proxy.
	 */
	int order() default Ordered.LOWEST_PRECEDENCE;

}

1 注解的作用

/**
 * Enables Spring's asynchronous method execution capability, similar to functionality
 * found in Spring's {@code <task:*>} XML namespace.
 *
 * <p>To be used together with @{@link Configuration Configuration} classes as follows,
 * enabling annotation-driven async processing for an entire Spring application context:
 
 使Spring有异步方法执行能力
 和@Configuration一起配合使用,在Spring应用程序上下文中能够注解驱动的异步功能

2 配置使用方式

 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableAsync
 * public class AppConfig {
 *
 * }</pre>
     
  (&#064;)代表@意思
   在配置类中使用@Configuration@EnableAsync

3 使用@Async注解, 使方法加入线程池中运行

 * {@code MyAsyncBean} is a user-defined type with one or more methods annotated with
 * either Spring's {@code @Async} annotation, the EJB 3.1 {@code @javax.ejb.Asynchronous}
 * annotation, or any custom annotation specified via the {@link #annotation} attribute.
 * The aspect is added transparently for any registered bean, for instance via this
 * configuration:
 *
 * <pre class="code">
 * &#064;Configuration
 * public class AnotherAppConfig {
 *
 *     &#064;Bean
 *     public MyAsyncBean asyncBean() {
 *         return new MyAsyncBean();
 *     }
 * }</pre>
     MyAsyncBean是一个自定义类型,添加@Async方法注解.
     

4 Spring默认会搜索一个相关的线程池

 * <p>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. If
 * neither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}
 * will be used to process async method invocations. Besides, annotated methods having a
 * {@code void} return type cannot transmit any exception back to the caller. By default,
 * such uncaught exceptions are only logged.
     
    Spring默认会搜索一个相关的线程池 
     定义: 二者中唯一的TaskExecutor或Executor中的"taskExecutor" 
     

5 自定义所有的配置

 * <p>To customize all this, implement {@link AsyncConfigurer} and provide:
 * <ul>
 * <li>your own {@link java.util.concurrent.Executor Executor} through the
 * {@link AsyncConfigurer#getAsyncExecutor getAsyncExecutor()} method, and</li>
 * <li>your own {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler
 * AsyncUncaughtExceptionHandler} through the {@link AsyncConfigurer#getAsyncUncaughtExceptionHandler
 * getAsyncUncaughtExceptionHandler()}
 * method.</li>
 * </ul>
 
     自定义所有的配置. 实现AsyncConfigurer接口,提供自己的线程池和异常处理器.
     也可以不实现该接口,在配置类中的方法加@Bean注解,在调用时使用@Async(bean),来指定运行此方法的线程池.

6 关于依赖和延迟加载

 * <p><b>NOTE: {@link AsyncConfigurer} configuration classes get initialized early
 * in the application context bootstrap. If you need any dependencies on other beans
 * there, make sure to declare them 'lazy' as far as possible in order to let them
 * go through other post-processors as well.</b>
     AsyncConfigurer配置类初始化比应用上下文引导文件bootstrap更早一点,如果有任何依赖其他的bean,需要尽可能声明延迟初始化,为了让他们经过后面的后置处理器.

7 示例代码

 * <pre class="code">
 * &#064;Configuration
 * &#064;EnableAsync
 * public class AppConfig implements AsyncConfigurer {
 *
 *     &#064;Override
 *     public Executor getAsyncExecutor() {
 *         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
 *         executor.setCorePoolSize(7);
 *         executor.setMaxPoolSize(42);
 *         executor.setQueueCapacity(11);
 *         executor.setThreadNamePrefix("MyExecutor-");
 *         executor.initialize();
 *         return executor;
 *     }
 *
 *     &#064;Override
 *     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
 *         return new MyAsyncUncaughtExceptionHandler();
 *     }
 * }</pre>
     
     实现AsyncConfigurer接口,自定义线程池,自定义异常处理器.

8 方法说明

annotation()方法

	/**
	 * Indicate the 'async' annotation type to be detected at either class
	 * or method level.
	 * <p>By default, both Spring's @{@link Async} annotation and the EJB 3.1
	 * {@code @javax.ejb.Asynchronous} annotation will be detected.
	 * <p>This attribute exists so that developers can provide their own
	 * custom annotation type to indicate that a method (or all methods of
	 * a given class) should be invoked asynchronously.
	 */
	Class<? extends Annotation> annotation() default Annotation.class;
	
在其他类上,async注解类型被检测出来,或者在方法层面上. 默认检测,Spring的@Async和EJB 3.1@Asynchronous  .开发者也可以提供自定义的注解类型,指明方法应该可以异步调用.

proxyTargetClass()方法

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies.
	 * <p><strong>Applicable only if the {@link #mode} is set to {@link AdviceMode#PROXY}</strong>.
	 * <p>The default is {@code false}.
	 * <p>Note that setting this attribute to {@code true} will affect <em>all</em>
	 * Spring-managed beans requiring proxying, not just those marked with {@code @Async}.
	 * For example, other beans marked with Spring's {@code @Transactional} annotation
	 * will be upgraded to subclass proxying at the same time. This approach has no
	 * negative impact in practice unless one is explicitly expecting one type of proxy
	 * vs. another &mdash; for example, in tests.
	 */
	boolean proxyTargetClass() default false;
	基于子类(CGLB)代理创建
        代理方式设置,默认false 只针对@Async注解的方法,如果true将在所有Spring管理的bean上生效.

mode()方法

	/**
	 * Indicate how async advice should be applied.
	 * <p><b>The default is {@link AdviceMode#PROXY}.</b>
	 * Please note that proxy mode allows for interception of calls through the proxy
	 * only. Local calls within the same class cannot get intercepted that way; an
	 * {@link Async} annotation on such a method within a local call will be ignored
	 * since Spring's interceptor does not even kick in for such a runtime scenario.
	 * For a more advanced mode of interception, consider switching this to
	 * {@link AdviceMode#ASPECTJ}.
	 */
	AdviceMode mode() default AdviceMode.PROXY;
	异步能生效的模式,默认为Porxy,另一种是ASPECTJ

order()方法

	/**
	 * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor}
	 * should be applied.
	 * <p>The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run
	 * after all other post-processors, so that it can add an advisor to
	 * existing proxies rather than double-proxy.
	 */
	int order() default Ordered.LOWEST_PRECEDENCE;
	指明处理的顺序,默认在所有其他的后置处理器后面运行,以至于他可以添加知道从已经存在的代理,而不是二次重复代理

9 导入的AsyncConfigurationSelector类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
	/**
	 * Returns {@link ProxyAsyncConfiguration} or {@code AspectJAsyncConfiguration}
	 * for {@code PROXY} and {@code ASPECTJ} values of {@link EnableAsync#mode()},
	 * respectively.
	 */
	@Override
	@Nullable
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return new String[] {ProxyAsyncConfiguration.class.getName()};
			case ASPECTJ:
				return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
			default:
				return null;
		}
	}
	selectImports在Spring容器扫描Bean的时候,根据传入的模型,选择要导入的类.
    默认是proxy,返回的类是ProxyAsyncConfiguration.
@Configuration(proxyBeanMethods = false)
@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 bpp = new AsyncAnnotationBeanPostProcessor();
		bpp.configure(this.executor, this.exceptionHandler);
		Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
		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是一个配置类, 追踪上面实现了BeanPostProcessor接口,向Spring容器添加了AsyncAnnotationBeanPostProcessor后置处理器,它的父类AbstractAdvisingBeanPostProcessor中定义了处理器

	//前置处理器
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		return bean;
	}

	//后置处理器
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (bean instanceof AopInfrastructureBean || this.advisor == null) {
			// Ignore AOP infrastructure such as scoped proxies.
			return bean;
		}

		if (bean instanceof Advised) {
			Advised advised = (Advised) bean;
			if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
				// Add our local Advisor to the existing proxy's Advisor chain...
				if (this.beforeExistingAdvisors) {
					advised.addAdvisor(0, this.advisor);
				}
				else {
					advised.addAdvisor(this.advisor);
				}
				return bean;
			}
		}

		if (isEligible(bean, beanName)) {
			ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
			if (!proxyFactory.isProxyTargetClass()) {
				evaluateProxyInterfaces(bean.getClass(), proxyFactory);
			}
			proxyFactory.addAdvisor(this.advisor);
			customizeProxyFactory(proxyFactory);
			return proxyFactory.getProxy(getProxyClassLoader());
		}

		// No async proxy needed.
		return bean;
	}

	前置处理器未做处理,直接返回;后置处理器对添加@Async的Bean进行了addAdvisor.

总结说明: @EnableAsync通过向Spring容器中引入后置处理器AsyncAnnotationBeanPostProcessor,在Bean创建过程中,对Bean进行advisor增强,对添加@Async注解的bean增强异步功能.

2@Async 源码说明

1 注解的作用

 * Annotation that marks a method as a candidate for <i>asynchronous</i> execution.
 * Can also be used at the type level, in which case all of the type's methods are
 * considered as asynchronous. Note, however, that {@code @Async} is not supported
 * on methods declared within a
 * {@link org.springframework.context.annotation.Configuration @Configuration} class.
 *
 * <p>In terms of target method signatures, any parameter types are supported.
 * However, the return type is constrained to either {@code void} or
 * {@link java.util.concurrent.Future}. In the latter case, you may declare the
 * more specific {@link org.springframework.util.concurrent.ListenableFuture} or
 * {@link java.util.concurrent.CompletableFuture} types which allow for richer
 * interaction with the asynchronous task and for immediate composition with
 * further processing steps.
 *
 * <p>A {@code Future} handle returned from the proxy will be an actual asynchronous
 * {@code Future} that can be used to track the result of the asynchronous method
 * execution. However, since the target method needs to implement the same signature,
 * it will have to return a temporary {@code Future} handle that just passes a value
 * through: e.g. Spring's {@link AsyncResult}, EJB 3.1's {@link javax.ejb.AsyncResult},
 * or {@link java.util.concurrent.CompletableFuture#completedFuture(Object)}.
     
   注解,标记一个方法作为一个候选的执行方法  ,也可以使用方法层次,让所有的方法作为异步方法    

2 执行运行被注解方法的线程池

	/**
	 * A qualifier value for the specified asynchronous operation(s).
	 * <p>May be used to determine the target executor to be used when executing
	 * the asynchronous operation(s), matching the qualifier value (or the bean
	 * name) of a specific {@link java.util.concurrent.Executor Executor} or
	 * {@link org.springframework.core.task.TaskExecutor TaskExecutor}
	 * bean definition.
	 * <p>When specified on a class-level {@code @Async} annotation, indicates that the
	 * given executor should be used for all methods within the class. Method-level use
	 * of {@code Async#value} always overrides any value set at the class level.
	 * @since 3.1.2
	 */
	String value() default "";

	一个限定值给一个特定的异步操作.通过匹配限定值,找到特定的Bean,执行目标方法.
        
     通常开发要实现AsyncConfigurer接口,自定义提供线程池和异常处理器.但也可以不实现该接口. 在配置类中方法上加@Bean注解,在调用方法上使用@Async(bean),来运行指定的线程池.

2 @EnableAsync和@Async使用

1继承AsyncConfigurer接口,自定义线程池和异常处理器

@Configuration
@EnableAsync
@Slf4j
public class MyAsync implements AsyncConfigurer {

    //核心线程数
    private static final int CORE_POOL_SIZE = 1;

    //最大线程数
    private static final int MAX_POOL_SIZE = 5;

    //队列大小
    private static final int QUEUE_CAPACITY = 50;

    //线程池中的线程的名称前缀
    private static final String THREAD_NAME = "MyThread-";


    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(CORE_POOL_SIZE);
        //配置最大线程数
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        //配置队列大小
        executor.setQueueCapacity(QUEUE_CAPACITY);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(THREAD_NAME);
        //配置线程池拒绝策略,设置为CallerRunsPolicy,当线程和队列都满了,由发起线程的主线程自己执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new MyAsyncUncaughtExceptionHandler();
    }

    private class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
            //处理的业务
            log.error("要输出报错信息");
        }
    }
}

2 不继承AsyncConfigurer接口,使用@Async(bean)方式

@Configuration
@EnableAsync
@Slf4j
public class MyAsync2 {
    
    //核心线程数
    private static final int CORE_POOL_SIZE = 1;

    //最大线程数
    private static final int MAX_POOL_SIZE = 5;

    //队列大小
    private static final int QUEUE_CAPACITY = 50;

    //线程池中的线程的名称前缀
    private static final String THREAD_NAME = "MyThread-";

    @Bean
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(CORE_POOL_SIZE);
        //配置最大线程数
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        //配置队列大小
        executor.setQueueCapacity(QUEUE_CAPACITY);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(THREAD_NAME);
        //配置线程池拒绝策略,设置为CallerRunsPolicy,当线程和队列都满了,由发起线程的主线程自己执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

3 案列

@RestController
public class UserController {
 
    @Autowired
    private UserService userService;

    @GetMapping("/test4")
    public void test4() {
        System.out.println("获取主线程名称:" + Thread.currentThread().getName());
        userService.serviceTest4();
        System.out.println("执行成功,返回结果");
    }
    
        @GetMapping("/test5")
    public void test5() {
        System.out.println("获取主线程名称:" + Thread.currentThread().getName());
        userService.serviceTest5();
        System.out.println("执行成功,返回结果");
    }
}
@Service
@EnableAsync
public class UserService {
/**
 * 采用异步执行
 */
 @Async("getAsyncExecutor")
public void serviceTest4() {
        // 这里执行实际的业务逻辑,在这里我们就是用一个简单的遍历来模拟
        Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach( t -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("获取number为:" + t) ;
        });
	}
    
     @Async
public void serviceTest5() {
        // 这里执行实际的业务逻辑,在这里我们就是用一个简单的遍历来模拟
        Arrays.stream(new int[]{1,2,3,4,5,6,7,8,9,10}).forEach( t -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("获取number为:" + t) ;
        });
	}
}

访问: http://localhost:8080/test4 返回结果如下:

获取主线程名称:http-nio-8080-exec-2
执行成功,返回结果
获取number为:1
获取number为:2
获取number为:3
获取number为:4
获取number为:5
获取number为:6
获取number为:7
获取number为:8
获取number为:9
获取number为:10

从结果来看.异步操作,多线程执行任务.先返回结果,后执行任务.

访问: http://localhost:8080/test5 返回结果如下:

获取主线程名称:http-nio-8080-exec-1
2021-04-15 [http-nio-8080-exec-1] [INFO ] o.s.scheduling.concurrent.ThreadPoolTaskExecutor -Initializing ExecutorService
执行成功,返回结果
获取number为:1
获取number为:2
获取number为:3
获取number为:4
获取number为:5
获取number为:6
获取number为:7
获取number为:8
获取number为:9
获取number为:10

从结果来看.异步操作,多线程执行任务.先返回结果,后执行任务. 结果与方法一相同.