参考资料:
在项目中, 偶尔需要使用异步的方式去执行任务.所以,我们可以引入多线程的使用,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">
* @Configuration
* @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">
* @Configuration
* public class AnotherAppConfig {
*
* @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">
* @Configuration
* @EnableAsync
* public class AppConfig implements AsyncConfigurer {
*
* @Override
* public Executor getAsyncExecutor() {
* ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
* executor.setCorePoolSize(7);
* executor.setMaxPoolSize(42);
* executor.setQueueCapacity(11);
* executor.setThreadNamePrefix("MyExecutor-");
* executor.initialize();
* return executor;
* }
*
* @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">
* <beans>
*
* <task:annotation-driven executor="myExecutor" exception-handler="exceptionHandler"/>
*
* <task:executor id="myExecutor" pool-size="7-42" queue-capacity="11"/>
*
* <bean id="asyncBean" class="com.foo.MyAsyncBean"/>
*
* <bean id="exceptionHandler" class="com.foo.MyAsyncUncaughtExceptionHandler"/>
*
* </beans>
* </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 — 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">
* @Configuration
* @EnableAsync
* public class AppConfig {
*
* }</pre>
(@)代表@意思
在配置类中使用@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">
* @Configuration
* public class AnotherAppConfig {
*
* @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">
* @Configuration
* @EnableAsync
* public class AppConfig implements AsyncConfigurer {
*
* @Override
* public Executor getAsyncExecutor() {
* ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
* executor.setCorePoolSize(7);
* executor.setMaxPoolSize(42);
* executor.setQueueCapacity(11);
* executor.setThreadNamePrefix("MyExecutor-");
* executor.initialize();
* return executor;
* }
*
* @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 — 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
从结果来看.异步操作,多线程执行任务.先返回结果,后执行任务. 结果与方法一相同.