乐趣区

关于springboot:Scheduled-定时任务

在开发中,有时候会遇到定时推送的需要,例如定时发送推送、定时发送邮件等等。SpringBoot 为咱们内置了定时工作,咱们只须要一个注解就能够开启定时为咱们所用了。

开启定时工作

在入口类 LogApplication 中增加 @EnableScheduling 注解,增加注解后 SpringBoot 就曾经认定了咱们要应用定时工作来实现一些业务逻辑了,外部会对应原始配置定时工作增加对应的配置文件。

@SpringBootApplication
@EnableScheduling    +    // 启用定时工作
public class LogApplication {

@Scheduled

配置定时工作非常简单,只须要在须要定时执行的办法上 增加 @Scheduled 注解即可 留神 该类上须要打上组件型注解 ,例如 @Componet, 这样该类才会被注入到 Spring 容器中进行治理,用来表明这是一个被 Spring 治理的 Bean,@Scheduled 才会失效。
当然个别会应用 @Coponent 的衍生注解:@Repository, @Service, @Controller 来代替,这里不开展讲了。

@Service  +
public class DayLogTaskImpl implements DayLogTask {@Scheduled(cron = "* * * * * ?")  +
    public void test1() {


当初来测试一下代码:

@Scheduled(cron = "* * * * * ?")
     public void test1() {
        /**
         * 每秒执行一次
         */
        logger.info("scheduler1 执行:" + System.currentTimeMillis());
    }

运行后果:

2022-02-14 13:46:50.000  INFO 2337 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : scheduler1 执行: 1644828410000
2022-02-14 13:46:51.003  INFO 2337 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : scheduler1 执行: 1644828411003
2022-02-14 13:46:52.000  INFO 2337 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : scheduler1 执行: 1644828412000
2022-02-14 13:46:53.000  INFO 2337 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : scheduler1 执行: 1644828413000

能够看到打印出的工夫相隔都大概是 1000 毫秒左右,合乎定义的每秒执行一次。

@Scheduled 注解参数阐明

cron: 该参数接管一个 cron 表达式,cron 表达式是一个字符串,如 示意每 5 秒执行一次。
zone: 时区。默认为服务器所在时区,接管类型为 java.util.TimeZone。
fixedDelay:上一次执行结束工夫点之后多长时间再执行。如:

@Scheduled(fixedDelay = 10000) // 上一次执行结束工夫点之后 10 秒再执行

fixedRate: 示意在上次执行开始之后多久后再次执行。用法同上。
initialDelay: 示意第一次工作执行提早多久。用法同上。

@Scheduled 的线程问题

@Scheduled 默认是单线程执行的,多个 @Scheduled 工作都用的同一个线程。如果某个工作是个耗时的操作,那么其它定时工作都会期待该工作的执行实现,从而造成梗塞。
比方上面咱们给出一个例子:两个无关的定时工作:打印日志 print 生成表格 generate,都是须要每秒触发一次。如果 generate 执行一次须要耗时 3 秒,则 printgenerate 都要等这一次的 generate 执行后能力再执行,这就达不到工作 print、generate 每秒执行一次的成果了。

咱们用代码来测试一下

咱们用 Thread.sleep()模仿执行办法时的耗时。两者都是每秒执行一次, 其中 test1 睡眠 3 秒模仿耗时。

 @Scheduled(cron = "* * * * * ?")
    public void test1() {
        /**
         * 每秒执行一次
         */
        logger.info("test1 执行并睡眠 3 秒:" + System.currentTimeMillis());
        try {Thread.sleep(3000);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        logger.info("test1 执行完结:" + System.currentTimeMillis());
    }

 @Scheduled(cron = "* * * * * ?")
    public void test2() {
        /**
         * 每秒执行一次
         */
        logger.info("test2 执行:" + System.currentTimeMillis());
    }

运行后果:

2022-02-14 14:09:41.004  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 执行并睡眠 3 秒: 1644829781004
2022-02-14 14:09:44.007  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 执行完结: 1644829784007
2022-02-14 14:09:44.010  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 执行: 1644829784010
2022-02-14 14:09:45.005  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 执行并睡眠 3 秒: 1644829785005
2022-02-14 14:09:48.007  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 执行完结: 1644829788007
2022-02-14 14:09:48.008  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 执行: 1644829788008
2022-02-14 14:09:49.009  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 执行: 1644829789009
2022-02-14 14:09:49.011  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 执行并睡眠 3 秒: 1644829789011
2022-02-14 14:09:52.012  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 执行完结: 1644829792012
2022-02-14 14:09:52.013  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 执行: 1644829792013
2022-02-14 14:09:53.005  INFO 2484 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test1 执行并睡眠 3 秒: 1644829793005

能够看到执行的线程都是[scheduling-1],并且 test2 执行距离并不固定是设置好的 1s,而是 有时是 1s,有时是 3s,4s。

那么,如何能让每个定时工作依照配置好的定时规定准时执行呢?这就须要咱们将定时工作配置成多线程的形式。

应用 @Async 异步执行办法

与增加定时工作雷同, 在入口类中增加 @EnableAsync,开启异步执行。在定时工作上增加 @Async 注解,标识该办法异步执行。办法代码与下面的雷同。

    @Async  +
    @Scheduled(cron = "* * * * * ?")
    public void test1() {

这时候咱们再执行代码看看成果:

2022-02-14 14:45:04.001  INFO 2608 --- [task-4] club.yunzhi.log.task.DayLogTask          : test1 执行并睡眠 3 秒: 1644831904001
2022-02-14 14:45:04.001  INFO 2608 --- [task-1] club.yunzhi.log.task.DayLogTask          : test1 执行完结: 1644831904001
2022-02-14 14:45:05.002  INFO 2608 --- [task-2] club.yunzhi.log.task.DayLogTask          : test1 执行完结: 1644831905002
2022-02-14 14:45:05.002  INFO 2608 --- [task-5] club.yunzhi.log.task.DayLogTask          : test1 执行并睡眠 3 秒: 1644831905002
2022-02-14 14:45:05.003  INFO 2608 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 执行: 1644831905003
2022-02-14 14:45:06.001  INFO 2608 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 执行: 1644831906001
2022-02-14 14:45:06.001  INFO 2608 --- [task-6] club.yunzhi.log.task.DayLogTask          : test1 执行并睡眠 3 秒: 1644831906001
2022-02-14 14:45:06.008  INFO 2608 --- [task-3] club.yunzhi.log.task.DayLogTask          : test1 执行完结: 1644831906008
2022-02-14 14:45:07.001  INFO 2608 --- [scheduling-1] club.yunzhi.log.task.DayLogTask          : test2 执行: 1644831907001
2022-02-14 14:45:07.001  INFO 2608 --- [task-4] club.yunzhi.log.task.DayLogTask          : test1 执行完结: 1644831907001
2022-02-14 14:45:07.002  INFO 2608 --- [task-7] club.yunzhi.log.task.DayLogTask          : test1 执行并睡眠 3 秒: 1644831907002

尽管办法执行须要耗时 3 秒,然而 每个工作还是依照每秒钟触发一次准时执行了。通过后面的 线程号 能够看出,工作的执行应用了不同的线程。test2 用的是 scheduling- 1 的线程号,每秒一次。而 test1 异步则用了多个线程号。

@Async 默认线程池个数为 8 个。
默认的 @Async 能够应酬个别的场景,但如果是并发量比拟高的状况下,就会存在肯定危险。例如开销过大、内存溢出等。为使服务运行稳固,咱们能够自定义配置线程池,而后让给须要异步执行的办法指定用该线程池运行。

简略来说就是配置一个 Executor,配置外围线程数,最大线程数,队列大小等信息。用 @Async(“myExecutor”)指定该调用该线程池。

@Configuration
public class ExecutorConfig {@Bean(name = "myExecutor")
  public Executor taskExecutor() {

具体能够参考这篇文章:https://www.interhorse.cn/a/3…

目前的我的项目中定时工作执行的办法较快,且距离在分钟级别,所以没有用到多线程定时工作,记录一下避免当前须要。

以上为定时工作的相干内容。

退出移动版