SpringBoot中的定时任务和异步任务注解

489 阅读5分钟

SpringBoot中的定时任务和异步任务注解 blog.csdn.net/jingqiu8809…

同步:需要等待,一个线程 异步:不需要等待,至少开启另外一个线程(异步和多线程不是一个概念) 串行:一个任务执行完,后面的任务才开启 并行:不管前面的任务,直接开启

同步串行:一个线程,多个任务一个接一个。阻塞队列。 同步并行:一个线程,多个任务,到点执行,会阻塞。场景少。 异步串行:多个线程,多个任务一个接一个。线程交替。 异步并行: 多个线程,多个任务,到点执行,不会阻塞。线程并发。

  1. Cron 表示每个xx执行一次。Cron表达式。cron都是按照整点来运行的,比如5秒一次,他会在0,5,10…秒运行,如果那个时间点还没运行结束,那么就会跳过这次任务。(如果卡点,正在执行,也跳过)
  2. fixedDelay非常好理解,它的间隔时间是根据上次的任务结束的时候开始计时的。比如一个方法上设置了fixedDelay=5*1000,那么当该方法某一次执行结束后,开始计算时间,当时间达到5秒,就开始再次执行该方法。
  3. fixedRate表示一个任务开始后间隔多长时间才执行下一个执行。比如设置 fixedRate=5*1000。那么如果一个任务8:00:00开始执行,不管什么时候执行结束,该任务的下一次执行的时间是8:00:05。如果执行时长超过间隔,那么会在任务执行结束后立刻执行下一次。

但是这里有个坑,当任务执行时长超过设置的间隔时长,那会是什么结果呢。打个比方,比如一个任务本来只需要花2秒就能执行完成,我所设置的fixedRate=5*1000,但是因为网络问题导致这个任务花了7秒才执行完成。当任务开始时Spring就会给这个任务计时,5秒钟时候Spring就会再次调用这个任务,可是发现原来的任务还在执行,这个时候第二个任务就阻塞了(这里只考虑单线程的情况下,多线程后面再讲),甚至如果第一个任务花费的时间过长,还可能会使第三第四个任务被阻塞。被阻塞的任务就像排队的人一样,一旦前一个任务没了,它就立马执行。

Spring 已经实现的异常线程池: 

  1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。 
  2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方 
  3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类 
  4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类 
  5. ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装

单例线程池:Executors.newSingleThreadExecutor(); 缓存线程池:Executors.newCachedThreadPool(); 定长线程池:Executors.newFixedThreadPool(nThreads); 定时线程池:Executors.newScheduledThreadPool(corePoolSize);

1.单例线程池:

单例线程池,顾名思义就是线程池中只有一个工作线程执行任务。执行的是串行操作,按照顺序执行。 如果有其他请求任务时,此时线程没有空闲,就会将此任务放到队列中等待执行。那它的队列其实是一个无线大的队列,如果有大量的请求任务时,一个线程的执行速度是有限的,就有可能造成队列积压,甚至于导致OOM的问题。

2.缓存线程池:

这种线程池内部没有核心线程,在有新的任务的时候,如果存在空闲线程的话就用空闲线程来执行任务,如果没有空闲线程的话,就新建一个线程来执行任务,这个最大线程数是接近无限大的。

缓存线程池的优点是可以灵活的伸缩线程数,但是它存在的问题是有可能在大量请求的时候,创建了过多的线程,导致的效率低下,甚至OOM。

3.定长线程池:

定长线程池的核心线程数等于最大线程数,所以它的线程数是固定的,是我们通过传参来进行设置的。当任务大于核心线程数的时候,就会放到队列中等待。

定长线程池需要注意的问题和单例线程池类似,也是有可能造成队列积压,以至于导致OOM的问题,所以,我们在使用时要合理的设置线程数。

4.定时线程池:

定时线程池可以指定时间的执行周期,指定一段时间去调用一次。 任务队列会根据任务延时时间的不同进行排序,延时时间越短地就排在队列的前面,先被获取执行。也就是优先级队列,这种队列是用堆来构建的,在此不做详述。

假设两个task ,task1, task2, 都是5秒进行一次。

@Schedule注解,同步, 默认只有一个线程。会有阻塞。 @Scheduled可以通过配置线程池,实现多线程处理。而且这种方式的多线程是不同任务之间的多线程,相同任务还是会排队执行。 @Async注解,异步,在spring中,默认使用SimpleAsyncTaskExecutor;不停生产新的。建议用ThreadPoolTaskExecutor重新定义 @Schedule+@Aync,异步非阻塞运行同时使用这两个注解的效果,相当于@Scheduled仅仅负责调度,而@Async指定的executor负责任务执行,不再使用调度器中的执行器来执行任务。task1和task2产生新的,task1之间也会产生新的。