Spring boot開啓定時任務的三種方式

零、前言

spring boot進行定時任務一共有三種方式,第一種也就是最簡單的一種:基於註解 (@Scheduled)的方式;第二種:基於接口 (SchedulingConfigurer);第三種:基於註解設定多線程定時任務

一、基於註解的方式

            首先,打開idea,創建springboot項目,無需引入任何jar,springboot自帶定時。

            然後,在啓動類中用註解@EnableScheduling進行標註,表明此類 存在定時任務。在定時執行的方法之上添加註解

@Scheduled(cron ="*/6 * * * * ?")。
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;


@SpringBootApplication
@EnableScheduling
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Scheduled(cron ="*/6 * * * * ?")
    public void sayHello() {
        System.out.println("hello");
    }
}

          點擊啓動,即可看到控制檯6秒輸出一次“hello”。

          當然,定時任務也可以放在其他類中。例如創建類Task1。

package com.example.task;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @Description
 * @ClassName Task1
 * @Author User
 * @date 2020.06.07 12:24
 */
@Component
public class Task1 {

    @Scheduled(cron ="*/1 * * * * ?")
    public void sayWord() {
        System.out.println("world");
    }
}

然後可以看到控制檯的輸出結果:

 

這裏有個要注意的細節,就是啓動類需要能掃描到定時任務類,否則定時任務啓動不起來。不僅需要@Component註解,也需要將啓動類位置位於定時任務類之上。如下圖:

筆者就是犯了這樣的錯,一直沒啓動起來。

@Scheduled除過cron還有三種方式:fixedRate,fixedDelay,initialDelay

cron:表達式可以定製化執行任務,但是執行的方式是與fixedDelay相近的,也是會按照上一次方法結束時間開始算起。

fixedRate:控制方法執行的間隔時間,是以上一次方法執行完開始算起,如上一次方法執行阻塞住了,那麼直到上一次執行完,並間隔給定的時間後,執行下一次。

@Configuration
@EnableScheduling    //開啓定時任務
public class ScheduleTask1 {
    //每3秒執行一次
    @Scheduled(fixedDelay = 3000)
    private void myTasks() {
        System.out.println("I do myself per third seconds");
    }
}

fixedRate:是按照一定的速率執行,是從上一次方法執行開始的時間算起,如果上一次方法阻塞住了,下一次也是不會執行,但是在阻塞這段時間內累計應該執行的次數,當不再阻塞時,一下子把這些全部執行掉,而後再按照固定速率繼續執行。

@Component
@EnableScheduling    //開啓定時任務
public class ScheduleTask2 {
    //每10秒執行一次
    @Scheduled(fixedRate = 10000)
    private void myTasks2() {
        System.out.println("我是一個定時任務");
    }
}

initialDelay:initialDelay = 10000 表示在容器啓動後,延遲10秒後再執行一次定時器。

@Component
@EnableScheduling    //開啓定時任務
public class ScheduleTask {
    //容器啓動後,延遲10秒後再執行一次定時器,以後每10秒再執行一次該定時器。
    @Scheduled(initialDelay = 10000, fixedRate = 10000)
    private void myTasks3() {
        System.out.println("我是一個定時任務3");
    }
}

二、cron解釋

cron

cron 用法跟linux下是一摸一樣的,如果你搞過linux下的定時,那麼必然很熟悉。

結構

cron表達式是一個字符串,分爲6或7個域,每兩個域之間用空格分隔,

其語法格式爲:"秒域 分域 時域 日域 月域 周域 年域"

取值範圍

域名 可取值 可取符號(僅列部分常用)
秒域 0~59的整數 * - , /
分域 0~59的整數 * - , /
時域 0~23的整數 * - , /
日域 1~31的整數 * - , / ? L
月域 1~12的整數或JAN~DEC * - , /
周域 1~7的整數或SUN~SAT * - , / ? L #
年域 1970~2099的整數 * - , /

常例

表達式 意義
每隔5秒鐘執行一次 */5 * * * * ?
每隔1分鐘執行一次 0 * /1 * * * ?
每天1點執行一次 0 0 1 * * ?
每天23點55分執行一次   0 55 23 * * ?
每月最後一天23點執行一次 0 0 23 L * ?
每週六8點執行一次 0 0 8 ? * L
每月最後一個週五,每隔2小時執行一次   0 0 */2 ? * 6L
每月的第三個星期五上午10:15執行一次 0 15 10 ? * 5#3
在每天下午2點到下午2:05期間的每1分鐘執行 0 0-5 14 * * ?
表示週一到週五每天上午10:15執行   0 15 10 ? * 2-6
每個月的最後一個星期五上午10:15執行 0 15 10 ? * 6L
每天上午10點,下午2點,4點執行一次 0 0 10,14,16 * * ?
朝九晚五工作時間內每半小時執行一次 0 0/30 9-17 * * ?
每個星期三中午12點執行一次   0 0 12 ? * 4
每年三月的星期三的下午2:10和2:44各執行一次 0 10,44 14 ? 3 4 
每月的第三個星期五上午10:15執行一次 0 15 10 ? * 6#3
每月一日凌晨2點30執行一次 0 30 2 1 * ?
每分鐘的第10秒與第20秒都會執行 10,20 * * * * ?
每月的第2個星期的周5,凌晨執行 0 0 0 ? * 6#2

本方法的demo地址: https://github.com/SUST-MaZhen/scheduledTask.git

三、基於接口的方式 

        使用@Scheduled 註解很方便,但缺點是當我們調整了執行週期的時候,需要重啓應用才能生效,這多少有些不方便。爲了達到實時生效的效果,那麼可以使用接口來完成定時任務,統一將定時器信息存放在數據庫中。

在mysql中執行一下腳本插入定時任務:

drop table if exists `scheduled`;
create table `scheduled` (
 `cron_id` varchar(30) NOT NULL primary key,
 `cron_name` varchar(30) NULL,
 `cron` varchar(30) NOT NULL
);
insert into `scheduled` values ('1','定時器任務一','0/6 * * * * ?');

創建一個springboot 項目:我們這裏只添加一個mapper,不要bean也不要service以及controller,只是爲了演示定時功能而已。demo結構:

數據源基本配置:application.properties

## mysql數據源配置
spring.datasource.url=jdbc:mysql://host:3306/dbname?useUnicode=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

## Mybatis 配置
# 配置爲 com.example.bean 指向實體類包路徑
#mybatis.typeAliasesPackage=com.zhenma.bean

mapper也就是dao:

package com.zhenma.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface CronMapper {
    @Select("select cron from scheduled where cron_id = #{id}")
    public String getCron(int id);
}

task類:

package com.zhenma.scheduled;

import com.zhenma.mapper.CronMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

/**
 * @Description
 * @ClassName MyTask
 * @Author User
 * @date 2020.06.07 15:23
 */
@Component
@EnableScheduling
public class MyTask implements SchedulingConfigurer {

    @Autowired
    protected CronMapper cronMapper;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.addTriggerTask(() -> process(),
                triggerContext -> {
                    String cron = cronMapper.getCron(1);
                    if (cron.isEmpty()) {
                        System.out.println("cron is null");
                    }
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                });
    }

    private void process() {
        System.out.println("基於接口定時任務");
    }
}

運行結果:

從結果中可以看出,是按照每6秒也就是數據庫中查詢的結果來進行的。

需求:我現在需要每10秒執行一次定時任務,該怎麼辦呢?對!只需要修改數據庫值即可,server無需重啓。觀察修改後的結果。

感覺好(。・∀・)ノ゙嗨哦。

demo地址:https://github.com/SUST-MaZhen/scheduledtask2.git 

四、 基於註解設定多線程定時任務

前面講到了@Scheduled執行週期任務會受到上次一個任務的執行時間影響。那麼可以開啓多線程執行週期任務。

創建springboot項目,創建一個多線程定時任務類如下:

package com.example.task;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * @Description
 * @ClassName MultiThreadTask
 * @Author User
 * @date 2020.06.07 18:56
 */
@EnableScheduling   // 1.開啓定時任務
@EnableAsync        // 2.開啓多線程
@Component
public class MultiThreadTask {

    @Async
    @Scheduled(fixedDelay = 1000)  //間隔1秒
    public void first() throws InterruptedException {
        System.out.println("第一個定時任務開始 : " + LocalDateTime.now().toLocalTime() + "\r\n線程 : " + Thread.currentThread().getName());
        Thread.sleep(1000 * 10);
    }

    @Async
    @Scheduled(fixedDelay = 2000)
    public void second() {
        System.out.println("第二個定時任務開始 : " + LocalDateTime.now().toLocalTime() + "\r\n線程 : " + Thread.currentThread().getName());
    }
}

 執行結果如下:

從結果可以看出:第一個任務的執行時間也不受其本身執行時間的限制。兩個任務也互不影響。

demo地址: https://github.com/SUST-MaZhen/scheduledtash3.git

五、總結

       本文介紹了spring boot創建定時任務的三種方式,當然還有其他方式,例如最近本的定時器來進行等等。記錄爲了使用,多使用便會記憶,便會理解。 寫個博客真費勁,卡在demo上...。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章