SpringBoot中的定時任務


一、基於注解(@Scheduled)

  • 基於注解@Scheduled默認為單線程,開啟多個任務時,任務的執行時機會受上一個任務執行時間的影響。

1. 創建定時器

  • 使用SpringBoot基於注解來創建定時任務非常簡單,只需幾行代碼便可完成。 代碼如下:
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    
    import java.time.LocalDateTime;
    
    /**
     * @author zhaokuii11@163.com
     * @create 2022-01-11 14:53
     * @Description
     */
    @Configuration    // 1. 代表當前類是一個配置類
    @EnableScheduling // 2.開啟定時任務
    public class StaticScheduleTask {
        //3.添加定時任務
        @Scheduled(cron = "0/5 * * * * ?")
        //或直接指定時間間隔,例如:5秒
        //@Scheduled(fixedRate=5000)
        private void configureTask() {
            System.out.println("執行靜態定時任務時間" + LocalDateTime.now());
        }
    }
    
    image

1.2 Cron表達式參數分別表示:

  1. 秒(0~59) 例如0/5表示每5秒
  2. 分(0~59)
  3. 時(0~23)
  4. 日(0~31)的某天,需計算
  5. 月(0~11)
  6. 周幾( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)
  • @Scheduled:除了支持靈活的參數表達式cron之外,還支持簡單的延時操作,例如 fixedDelay ,fixedRate 填寫相應的毫秒數即可。
    # Cron表達式范例:
    每隔5秒執行一次:*/5 * * * * ?
    每隔1分鍾執行一次:0 */1 * * * ?
    每天23點執行一次:0 0 23 * * ?
    每天凌晨1點執行一次:0 0 1 * * ?
    每月1號凌晨1點執行一次:0 0 1 1 * ?
    每月最后一天23點執行一次:0 0 23 L * ?
    每周星期天凌晨1點實行一次:0 0 1 ? * L
    在26分、29分、33分執行一次:0 26,29,33 * * * ?
    每天的0點、13點、18點、21點都執行一次:0 0 0,13,18,21 * * ?
    
  • 更詳細的 cron參數
    https://blog.csdn.net/Qiwan2/article/details/89848298
    https://blog.csdn.net/weixin_30720461/article/details/91388486

二、基於接口(SchedulingConfigurer)

  • 從數據庫中讀取指定時間來動態執行定時任務,這時候基於接口的定時任務就派上用場了。
  • 基於接口(SchedulingConfigurer)

2.1 依賴

    <!--web啟動器-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--添加MySql依賴 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.45</version>
        <scope>runtime</scope>
    </dependency>
    <!--jdbc-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!--添加Mybatis依賴 配置mybatis的一些初始化的東西-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>

2.2 創建數據庫

  • 開啟本地數據庫mysql,隨便打開查詢窗口,然后執行腳本內容,如下:
DROP DATABASE IF EXISTS `socks`;
CREATE DATABASE `socks`;
USE `SOCKS`;
DROP TABLE IF EXISTS `cron`;
CREATE TABLE `cron`  (
  `cron_id` varchar(30) NOT NULL PRIMARY KEY,
  `cron` varchar(30) NOT NULL  
);
INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');
# 連接參數配置
spring.datasource.url=jdbc:mysql://localhost:3306/socks
spring.datasource.password=root
spring.datasource.username=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

2.3 創建定時器

  • 數據庫准備好數據之后,我們編寫定時任務,注意這里添加的是TriggerTask,目的是循環讀取我們在數據庫設置好的執行周期,以及執行相關定時任務的內容。
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.context.annotation.Configuration;
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.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * @author zhaokuii11@163.com
 * @create 2022-01-11 15:10
 * @Description
 */
@Configuration    //1. 主要用於標記配置類,兼備 Component的效果
@EnableScheduling //2. 開啟定時任務
public class DynamicScheduleTask implements SchedulingConfigurer {
    @Mapper
    public interface CronMapper {
        @Select("select cron from cron limit 1")
        public String getCron();
    }

    @Resource
    CronMapper cronMapper;

    /**
     * 執行定時任務
     *
     * @param scheduledTaskRegistrar
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        scheduledTaskRegistrar.addTriggerTask(
                //1. 添加任務內容(Runnable)
                () -> System.out.println("動態定時任務:" + LocalDateTime.now().toLocalTime()),
                //2. 設置執行周期(Trigger)
                triggerContext -> {
                    //2.1 從數據庫獲取執行周期
                    String cron = cronMapper.getCron();
                    //2.2 合法性校驗
                    if (StringUtils.isEmpty(cron)) {
                        // Omitted Code ...
                    }
                    //2.3 返回執行周期(Date)
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                });
    }
}

2.4 啟動測試

  • 啟動應用后,查看控制台,打印時間是我們預期的每5秒一次:
  • 注意: 如果在數據庫修改時格式出現錯誤,則定時任務會停止,即重新修改正確;此時只能重新啟動項目才能恢復。
    image

三、基於注解設定多線程定時任務

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;

/**
 * @author zhaokuii11@163.com
 * @create 2022-01-11 18:55
 * @Description
 */
@Component //@Component注解用於對那些比較中立的類進行注釋
@EnableScheduling //1. 開啟定時任務
@EnableAsync //2. 開啟多線程
public class MultithreadScheduleTask {

    @Async//3. @Async就可以定義一個線程任務
    @Scheduled(fixedDelay = 1000)
    public void first() throws InterruptedException {
        System.out.println("第一個定時任務開始" + LocalDateTime.now().toLocalTime());
        Thread.sleep(1000 * 10);
    }

    @Async
    @Scheduled(fixedDelay = 1000)
    public void second() throws InterruptedException {
        System.out.println("第二個定時任務開始" + LocalDateTime.now().toLocalTime());
        System.out.println();
    }

}

image

  • 從控制台可以看出,第一個定時任務和第二個定時任務互不影響;
    並且,由於開啟了多線程,第一個任務的執行時間也不受其本身執行時間的限制,所以需要注意可能會出現重復操作導致數據異常。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM