1.功能说明
Spring提供了Async注解来实现方法的异步调用。
即当调用Async标识的方法时,调用线程不会等待被调用方法执行完成即返回继续执行以下操作,而被调用的方法则会启动一个独立线程来执行此方法。
这种异步执行的方式通常用于处理接口中不需要返回给用户的数据处理。比如当注册的时候,只需要将用户信息返回用户,而关于信息的保存操作可以使用异步执行。
Spring提供了Scheduled注解来实现定时任务的功能。
在异步方法和定时任务功能中都是开发这自己定义需要执行的方法,然后交给Spring容器管理线程,并执行相应的方法。在使用异步方法和定时任务的时候需要特别注意的是线程池的配置以及任务中异常的处理。下面对这两个功能进行简单介绍。
2.关键注解和配置接口
功能开启注解:
EnableAsync和EnableScheduling
通过在Spring的配置类中添加这两个注解来开启Spring的异步方法和定时任务的功能。
异步方法标识注解Async,其定义为:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
String value() default "";
}
在注解定义中可以看到此注解可以用于type和method,当此注解用于类的时候,表示此类中的所有方法都为异步方法。此注解中的value属性可用于指定执行此异步方法的线程池。线程池的具体确定方法下面具体分析。
定时任务标识注解Scheduled,定义如下:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
定时任务
String cron() default "";
String zone() default "";
//上次执行结束到下次执行开始
longfixedDelay() default -1;
String fixedDelayString() default "";
上次执行开始到本次执行开始
longfixedRate() default -1;
fixedRate或者fixedDelay的时候第一次的延迟时间
longinitialDelay() default -1;
}
3.Spring线程池的选择和自定义配置线程池
在项目中我们通常不会自己手动创建线程,而是通过统一的线程池来执行task或者异步方法,使用这种方法来避免多人团队中由于自定义线程导致的资源耗尽的问题。在自定义线程池之前首先要了解Spring 在执行异步任务或者方法的时候是怎么选择线程池的。
3.1 Async对于线程池的选择顺序
Async线程池的选择顺序如下图所示:
Spring在执行async标识的异步方法的时候首先会在Spring的上下文中搜索类型为TaskExecutor或者名称为“taskExecutor”的bean,当可以到的时候,就将任务提交到此线程池中执行。当不存在以上线程池的时候,Spring会手动创建一个SimpleAsyncTaskExecutor执行异步任务。
另外当标识async注解的时候指定了,value值,Spring会使用指定的线程池执行。比如以下:@Async(value = "asyncTaskThreadPool")
这个时候Spring会去上下文中名字为asyncTaskThreadPool的bean,并执行异步任务,不到,会抛出异常。
3.2 Scheduled对于线程池的选择顺序
Scheduled对于线程池的选择顺序如下图所示:
当Spring执行定时任务的时候,首先会在上下文中类型为TaskScheduler或者名称为taskScheduler 的bean,不到的时候会手动创建一个线程执行此task。
3.3 自定义线程池和异常处理
在了解了Spring对于线程池的选择后,我们需要自定义线程池。自定义Async线程池有三种方式。
方法一:首先配置接口,重写获取线程池的方法。
配置Async方法的线程池需要继承AsyncConfigurerSupport类,或者实现AsyncConfigurer接口,并重写getAsyncExecutor方法,代码如下:
@Configuration
@EnableAsync
public class ThreadPoolBeanFactory extends AsyncConfigurerSupport{
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutorasyncTaskThreadPool = new ThreadPoolTaskExecutor();
asyncTaskThreadPool.setCorePoolSize(100);
asyncTaskThreadPool.setMaxPoolSize(200);
asyncTaskThreadPool.setQueueCapacity(11);
spring boot选择题asyncTaskThreadPool.setThreadFactory(new ThreadFactory() {
private final AtomicLong index = new AtomicLong(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Async-override-task-pool-thread-" + AndIncrement());
}
});
asyncTaskThreadPool.initialize();
returnasyncTaskThreadPool;
}
@Override
publicAsyncUncaughtExceptionHandlergetAsyncUncaughtExceptionHandler() {
//返回值为void的异步方法不会传递异常,当方法中出现异常的时候只会打印日志,重写此方法来自定义异常处理方法
return null;
}
}
这种定义的方法缺点是没有定义bean。
方法二:自定义相应类型的线程池bean。
第二种方法是基于Spring对线程选择的原理来实现的,定义一个类型为TaskExecutor的bean,定义方式如下:
@Bean
publicTaskExecutorasyncTaskThreadPool() {
ThreadPoolTaskExecutorasyncTaskThreadPool = new ThreadPoolTaskExecutor();
asyncTaskThreadPool.setCorePoolSize(100);
asyncTaskThreadPool.setMaxPoolSize(200);
asyncTaskThreadPool.setThreadFactory(new ThreadFactory() {
private final AtomicLong index = new AtomicLong(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "Async-task-pool-thread-" + AndIncrement());
}
});
// asyncTaskThreadPool.initialize();//当为bean的时候不需要调用此方法,装载容器的时候回自动调用
returnasyncTaskThreadPool;
}
以上两种方式定义线程池的时候在定义异步方法可以不执行线程池。定义如下:
@Async
public void test(){
System.out.println(Thread.currentThread().getName());
}
此时Spring会自动使用以上定义的线程池执行此方法。使用以上两种配置输出结果依次是:
Async-task-pool-thread-1
Async-task-override-pool-thread-1
方法三在Async注解中执行线程池名称
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论