Spring关于@Scheduled限制的问题

目录
  • Spring @Scheduled限制
  • Spring多定时任务@Scheduled执行阻塞
    • 一. 问题描述
    • 二. 场景复现
    • 三. 解决方案
    • 四. 总结

Spring @Scheduled限制

@Scheduled具有一定的限制性,它毕竟不是quartz,只是简单的定时,比jdk Timer就加入了线程池而以

  • @Scheduled 不支持年份定时
  • @Scheduled 不支持W L这些字母

没办法 如果非要使用那就只能放弃注解使用XML方式了

Spring多定时任务@Scheduled执行阻塞

一. 问题描述

最近项目中发现一个问题,计划每日凌晨4:40执行一个定时任务,使用注解方式: @Scheduled(cron = “0 40 4 * * ?”),cron表达式明显没有问题,但是这个定时任务总是不按时执行,有时候得等到8点多,有时候9点多才执行。后来查了下,原来这种定时方式默认是单线程执行的,恰好我这里有多个定时任务,并且其中有个在4:40之前的定时任务比较耗时,导致4:40的任务只能等待之前的任务执行完成才能够触发,所以要自己手动把定时任务设置成多线程的方式才行。

二. 场景复现

项目描述:使用Springboot进行开发

设置两个定时任务,每5s执行一次,并打印出其执行情况

代码如下:

@Component
@Log4j2
public class ScheduledTask {
    @Scheduled(cron = "0/5 * * * * ?")
    public void task1() throws InterruptedException {
        log.info("I am task11111111, current thread: {}", Thread.currentThread());
        while (true) {
            //模拟耗时任务,阻塞10s
            Thread.sleep(10000);
            break;
        }
    }
    @Scheduled(cron = "0/5 * * * * ?")
    public void task2() {
        log.info("I am task22222222, current thread: {}", Thread.currentThread());
    }
}

执行结果如下:

2019-04-24 17:11:15.008 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:15.009 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:25.009 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:30.002 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:30.003 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[scheduling-1,5,main]
2019-04-24 17:11:40.004 INFO 16868 --- [ scheduling-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[scheduling-1,5,main]

由结果可见,task1与task2由同一个线程Thread[scheduling-1,5,main]执行,也即该定时任务默认使用单线程,并且由于task1阻塞了10s,导致本应5s执行一次的定时任务10s才执行一次。

三. 解决方案

网上有多种解决方案,以下列举两种

方案一:使用@Async注解实现异步任务

这种方式比较简单,在定时任务上加上@Async注解,注意:需启动类配合加上 @EnableAsync才会生效

代码如下:

@Component
@Log4j2
public class ScheduledTask {
    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void task1() throws InterruptedException {
        log.info("I am task11111111, current thread: {}", Thread.currentThread());
        while (true) {
            //模拟耗时任务,阻塞10s
            Thread.sleep(10000);
            break;
        }
    }
    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void task2() {
        log.info("I am task22222222, current thread: {}", Thread.currentThread());
    }
}

运行结果:

2019-04-24 17:03:00.024 INFO 2152 --- [ task-1] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[task-1,5,main]
2019-04-24 17:03:00.024 INFO 2152 --- [ task-2] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[task-2,5,main]
2019-04-24 17:03:05.001 INFO 2152 --- [ task-3] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[task-3,5,main]
2019-04-24 17:03:05.001 INFO 2152 --- [ task-4] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[task-4,5,main]
2019-04-24 17:03:10.002 INFO 2152 --- [ task-5] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[task-5,5,main]
2019-04-24 17:03:10.003 INFO 2152 --- [ task-6] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[task-6,5,main]

由运行日志可见,定时每5s执行一次已生效,且每次任务使用的线程不一样,也即实现了多线程执行定时任务,不会出现任务等待现象。此方式据说默认线程池大小为100,要是任务不多的话有点大材小用了,所以我觉得第二种方式比较好。

方案二:手动设置定时任务的线程池大小

定时任务代码部分还原,不使用@Async注解,新增启动代码配置:

@Configuration
public class AppConfig implements SchedulingConfigurer {
    @Bean
    public Executor taskExecutor() {
     //指定定时任务线程数量,可根据需求自行调节
        return Executors.newScheduledThreadPool(3);
    }
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.setScheduler(taskExecutor());
    }
}

运行结果如下:

2019-04-24 17:26:15.008 INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:15.008 INFO 2164 --- [pool-1-thread-1] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[pool-1-thread-1,5,main]
2019-04-24 17:26:20.002 INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:25.001 INFO 2164 --- [pool-1-thread-2] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-2,5,main]
2019-04-24 17:26:30.001 INFO 2164 --- [pool-1-thread-1] com.example.demo.task.ScheduledTask : I am task11111111, current thread: Thread[pool-1-thread-1,5,main]
2019-04-24 17:26:30.001 INFO 2164 --- [pool-1-thread-3] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-3,5,main]
2019-04-24 17:26:35.001 INFO 2164 --- [pool-1-thread-3] com.example.demo.task.ScheduledTask : I am task22222222, current thread: Thread[pool-1-thread-3,5,main]

由结果可见,第二种方式也实现了多线程任务调度。

四. 总结

两种方式各有优缺点:

比较 方案一 方案二
优点 注解方式使用简单,代码量少 配置灵活,线程数可控
缺点 线程数不可控,可能存在资源浪费 需要增加编码

留个坑,从日志上看@Async方式针对同一任务也是异步的,也即task1每5s会执行一次,但是方式二貌似对同一个任务不会生效,task1执行的时候需等待上一次执行结束才会触发,并没有每5s执行一次。关于这个现象,下次再琢磨…

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring的@Scheduled 如何动态更新cron表达式

    常见的本地定时写法如下: @Scheduled(cron = "0/5 * * * * ?") private void test() { log.info("业务处理逻辑...5秒一次"); } 如果想要动态更新cron表达式,可以这样写: 先写一个类,让cron表达式总是读成员变量的值. 再写一个controller,通过调用set方法就可以动态设置这个cron表达式了 @Lazy(false) @Component @EnableScheduling publ

  • Spring内置定时任务调度@Scheduled使用详解

    Spring提供了@Scheduled注解用于定时任务. 一.@Scheduled的基本使用 启用调度支持:@EnableScheduling 可以将@Scheduled注释与触发器元数据一起添加到方法中.例如,以下方法每隔5秒调用一次,并具有固定的延迟,这意味着周期是从前面每次调用的完成时间开始计算的 @Scheduled(fixedDelay=5000) public void doSomething() { // something that should execute periodic

  • 我劝你谨慎使用Spring中的@Scheduled注解

    目录 引言 1.@Scheduled失效原因 2.解析流程图 3.使用新的方法 schedule定时任务修改表达式无效 引言 在一些业务场景中需要执行定时操作来完成一些周期性的任务,比如每隔一周删除一周前的某些历史数据以及定时进行某项检测任务等等. 在日常开发中比较简单的实现方式就是使用Spring的@Scheduled(具体使用方法不再赘述)注解. 但是在修改服务器时间时会导致定时任务不执行情况的发生,解决的办法是当修改服务器时间后,将服务进行重启就可以避免此现象的发生. 本文将主要探讨服务器

  • Spring Boot 2.x基础教程之使用@Scheduled实现定时任务的方法

    我们在编写Spring Boot应用中经常会遇到这样的场景,比如:我需要定时地发送一些短信.邮件之类的操作,也可能会定时地检查和监控一些标志.参数等. 创建定时任务 在Spring Boot中编写定时任务是非常简单的事,下面通过实例介绍如何在Spring Boot中创建定时任务,实现每过5秒输出一下当前时间. 在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置 @SpringBootApplication @EnableScheduling publi

  • spring task @Scheduled注解各参数的用法

    目录 参数详解 1. cron 2. zone 3. fixedDelay 4. fixedDelayString 5. fixedRate 6. fixedRateString 7. initialDelay 8. initialDelayString spring @Scheduled注解使用误区 @Scheduled注解的使用这里不详细说明,直接对8个参数进行讲解. 参数详解 1. cron 该参数接收一个cron表达式,cron表达式是一个字符串,字符串以5或6个空格隔开,分开共6或7个

  • SpringBoot执行定时任务@Scheduled的方法

    在做项目时,需要一个定时任务来接收数据存入数据库,后端再写一个接口来提供该该数据的最新的那一条. 数据保持最新:设计字段sign的值(0,1)来设定是否最新 定时任务插入数据:首先进行更新,将所有为1即新数据设置过期,然后插入新数据,设置sign为1.这两个操作是原子操作.通过添加事务来进行控制. Java 定时任务的几种实现方式 基于 java.util.Timer 定时器,实现类似闹钟的定时任务 使用 Quartz.elastic-job.xxl-job 等开源第三方定时任务框架,适合分布式

  • Spring中@Scheduled和HttpClient的连环坑

    前言 本文主要给大家介绍了关于Spring中@Scheduled和HttpClient的坑,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 曾经踩过一个大坑: 由于业务特殊性,会定时跑很多定时任务,对业务数据进行补偿操作等. 在Spring使用过程中,我们可以使用@Scheduled注解可以方便的实现定时任务. 有一天早上突然发现,从前一天晚上某一时刻开始,所有的定时任务全部都卡死不再运行了. @Scheduled默认单线程 经排查后发现,我们使用@Scheduled注解默认的

  • Spring中@Scheduled功能的使用方法详解

    目录 前言 一.Spring @Scheduled Annotation 1.2 如何启用@Scheduled 注释 1.3 使用@Scheduled 注释 二.固定的延时和频率使用@Scheduled 三.配合cron表达式使用@Scheduled 四.使用properties文件配置Cron 五.使用context配置Cron 总结 前言 Spring 为任务调度和基于使用@Scheduled 注释的 cron 表达式的异步方法执行提供了极好的支持.可以将@Scheduled 注释与触发器元

  • 多线程Spring通过@Scheduled实现定时任务

    目录 前言 一 定时任务调度注解@Scheduled 二 使用@Async实现异步调度 建立spring线程池 为异步调度方法指定线程池 前言 技术的入门大多比较简单,把别人的代码复制过来,删删改改,基本也就能实现个功能,查看个API大概也就知道如何实现几个功能,但是,如果对一项技术了解的足够深入,就要知道一个技术的优缺点,以及他存在的问题,这些就需要大量的时间及思考,疾风知劲草,只有足够了解才能临危不惧,才能在如疾风般强劲的bug来临时微微一笑,绝对不倒! 一 定时任务调度注解@Schedul

  • spring 定时任务@Scheduled详解

    一.配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org

  • Spring Boot @Scheduled定时任务代码实例解析

    假设我们已经搭建好了一个基于Spring Boot项目,首先我们要在Application中设置启用定时任务功能@EnableScheduling. 启动定时任务 package com.scheduling; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframewo

  • Spring关于@Scheduled限制的问题

    目录 Spring @Scheduled限制 Spring多定时任务@Scheduled执行阻塞 一. 问题描述 二. 场景复现 三. 解决方案 四. 总结 Spring @Scheduled限制 @Scheduled具有一定的限制性,它毕竟不是quartz,只是简单的定时,比jdk Timer就加入了线程池而以 @Scheduled 不支持年份定时 @Scheduled 不支持W L这些字母 没办法 如果非要使用那就只能放弃注解使用XML方式了 Spring多定时任务@Scheduled执行阻

  • 关于spring中定时器的使用教程

    前言 在很多实际的web应用中,都有需要定时实现的服务,如每天12点推送个新闻,每隔一个小时提醒用户休息一下眼睛,隔一段时间检测用户是否离线等等. spring框架提供了对定时器的支持,通过配置文件就可以很好的实现定时器,只需要应用启动,就自动启动定时器.下面介绍一下具体做法. 第一种,使用XML配置的方法 前期工作,配置spring的开发环境(这里用到了spring的web应用包,需要导入) 首先创建定时器的任务类,定时器要做什么工作,就在这里写什么方法. package org.time;

随机推荐