【Spring】异步任务注解@Async、@EnableAsync、@EnableScheduler、@Scheduled

前言

Spring中用@Async注解标记的方法,称为异步方法,其实就相当于我们自己在当前方法里面:new Thread(()-> System.out.println("hello world !"))
使用@Async注解的时候一定要在类上加@EnableAsync(注解形式),@EnableAsync注解的意思是开启支持异步,就是开启支持多线程的意思。可以标注在方法、类上。
@Scheduled、@EnableScheduler与@Async@EnableAsync类似。

使用

spring的xml配置如下:

<!-- 使@Async和@Scheduled生效,相当于注解形式的@EnableAsync+@EnableScheduling -->
<task:annotation-driven scheduler="taskScheduler" executor="taskExecutor"/>
<!-- 调度线程池配置(调度被@Scheduled注解的方法,若方法上没有@Async注解就立即在本scheduler线程执行该方法,若有就在executor线程池中取一个线程执行该方法),若不配置,多任务下会有阻塞问题,因为默认是1,所有声明的任务都是串行调度的 -->
<task:scheduler id="taskScheduler" pool-size="6"/>
<task:scheduler id="testScheduler" pool-size="6" />
<!-- 执行线程池配置(执行被@Asyn注解的方法),若不配置,某个任务到了第N次执行的时间但是第N-1还未执行完时会有阻塞问题(即使设置了fixRate),因为默认是1,任务的多次执行是串行执行的 -->
<task:executor id="taskExecutor" pool-size="2" />
<task:executor id="testExecutor" pool-size="10" />

对于task:scheduler

  1. 如果没有配置task:scheduler,则task:annotation-driven无需指定scheduler,且默认是TaskScheduler,线程名称:[pool-1-thread-1]
  2. 如果配置了一个task:scheduler,则task:annotation-driven无需指定scheduler,且默认是配置的这个scheduler(即会自动发现已配置的scheduler),线程名称:[{scheduler名称}-1]。
  3. 如果配置了多个task:scheduler,且task:annotation-driven未指定scheduler,则task:annotation-driven会自动发现多个scheduler导致报错说必须指定一个scheduler。

对于task:executor

  1. 如果没有配置task:executor,则task:annotation-driven无需指定executor,且默认是SimpleAsyncTaskExecutor(容易出现内存溢出,参考@Async默认线程池导致OOM问题),线程名称:[SimpleAsyncTaskExecutor-1]
  2. 如果配置了一个或多个task:executor,且task:annotation-driven未指定executor,且默认是SimpleAsyncTaskExecutor(即不会自动发现已配置的executor),若指定了就是指定的那一个,线程名称:[{executor名称}-1]。

@Async注解使用的基本方法:

  1. 在方法上添加@Async注解,且必须是public方法;
  2. 所使用的@Async注解方法的类对象应该是Spring容器管理的bean;
  3. 调用异步方法的类上需要配置上注解@EnableAsync(注解形式)

异步不生效问题

不要同类方法调用

异步方法所在类的另一个方法调用这个异步方法,异步不会生效!

原因是如果调用方和被调用方是在同一个类中,是无法产生切面的,@Async没有被Spring容器管理。

重复扫描

component-scan扫描或注解扫描时,要注意过滤,避免重复实例化,因为存在覆盖问题,@Async就失效了

没有定义线程池

如果不自定义异步方法的线程池默认使用SimpleAsyncTaskExecutorSimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。并发大的时候会产生严重的性能问题。
定义通用线程池

@EnableAsync
@Configuration
public class ThreadTestConfig {
  @Bean
  public Executor taskExecutor() {
    ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
    pool.setCorePoolSize(5); //线程池活跃的线程数
    pool.setMaxPoolSize(10); //线程池最大活跃的线程数
    pool.setWaitForTasksToCompleteOnShutdown(true);
    pool.setThreadNamePrefix("lalala");
    return pool;
  }
}

@Async默认使用Bean NametaskExecutor的线程池。也可以根据Bean Name指定特定线程池

@Async("taskExecuters2")
public void asyncMethod() {
  sleep(10);
  System.out.println("async");
}

源码分析

参考另一篇基于SpringBoot分析的文章【SpringBoot】@Async、AsyncConfigurer源码解析

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值