1. 程式人生 > 其它 >使用ScheduledExecutorService執行緒池手動動態控制定時任務

使用ScheduledExecutorService執行緒池手動動態控制定時任務

背景

在日常開發過程中,使用定時任務去執行一些業務邏輯是很常見的一種場景。比如定時傳送簡訊,郵件,電商系統的定時自動收貨、定時上下架功能等等。

一般實現定時任務有以下幾種方案:

JDK自帶

  • JDK自帶的Timer:這是java自帶的java.util.Timer類,這個類允許你排程一個java.util.TimerTask任務。使用這種方式可以讓你的程式按照某一個頻度執行,一般用的較少。
  • JDK1.5+ 新增的ScheduledExecutorService:是基於執行緒池設計的定時任務類,每個排程任務都會分配到執行緒池中的一個執行緒去執行,也就是說,任務是併發執行,互不影響。

第三方框架

  使用 Quartz、elastic-job、xxl-job 等開源第三方定時任務框架,適合分散式專案應用。不過配置起來稍顯複雜,不太易上手。
Spring Task

  Spring3.0以後自帶的task,可以將它看成一個輕量級的Quartz,而且使用起來比Quartz簡單許多。使用 Spring 提供的一個註解 @Schedule即可,開發簡單,使用比較方便。

本文主要向大家介紹使用ScheduledExecutorService操作定時任務。

使用

1.啟動類新增@EnableScheduling註解

2.建立定時任務類

ScheduleController.java

/** 
 * <p>
 *  介面控制定時任務開始和停止
 * </p>
 *
 * @className ScheduleController
 * 
@author Sue * @date 2021/7/28 **/ @RestController @RequestMapping("/task") public class ScheduleController { ScheduleTaskService scheduleTaskService; public ScheduleController(ScheduleTaskService scheduleTaskService) { this.scheduleTaskService = scheduleTaskService; } @PostMapping(
"/startCron") public R startCron() { scheduleTaskService.startCorn(); return R.ok("定時任務啟動成功!"); } @PostMapping("/stopCron") public R stopCron() { scheduleTaskService.stopCorn(); return R.ok("定時任務關閉成功!"); } }

ScheduleTaskService.java

/**
 * <p>
 *  定時任務
 * </p>
 *
 * @className ThreadPoolTaskSchedulerService
 * @author Sue
 * @create 2021/7/21 
 **/
public interface ScheduleTaskService {
    /**
     * <p>
     *  開始定時任務
     * </p>
     *
     * @return boolean
     * @author Sue
     * @date 2021/7/21
     */
    boolean startCorn();

    /**
     * <p>
     *  關閉定時任務
     * </p>
     *
     * @return boolean
     * @author Sue
     * @date 2021/7/21
     */
    boolean stopCorn();
}

ScheduleTaskServiceImpl.java

/**
 * <p>
 *  定時任務
 * </p>
 *
 * @className ThreadPoolTaskSchedulerServiceImpl
 * @author Sue
 * @create 2021/7/21 
 **/
@Slf4j
@Service
public class ScheduleTaskServiceImpl implements ScheduleTaskService {

    private ScheduledFuture<?> future;

    ThreadPoolTaskScheduler threadPoolTaskScheduler;

    public ScheduleTaskServiceImpl(ThreadPoolTaskScheduler threadPoolTaskScheduler) {
        this.threadPoolTaskScheduler = threadPoolTaskScheduler;
    }

    @Override
    public boolean startCorn() {
        if (future != null) {
            future.cancel(true);
            log.info("定時任務已停止");
        }
        //每10秒執行一次
        String cornConfig = "0/10 * * * * *";
        future = threadPoolTaskScheduler.schedule(new ScheduledTaskRunnable(), new CronTrigger(cornConfig));
        log.info("定時任務開啟");
        return false;
    }

    @Override
    public boolean stopCorn() {
        if (future != null) {
            future.cancel(true);
            log.info("定時任務已停止");
        }
        return false;
    }

    static class ScheduledTaskRunnable implements Runnable {
        @Override
        public void run() {
            //需要執行的業務邏輯
            log.info("定時任務開始執行,每10秒執行一次,當前時間{}", LocalDateTimeUtil.format(LocalDateTimeUtil.now(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }

    }
}

3.專案啟動後,呼叫介面,控制定時任務的啟動和停止

測試

呼叫startCorn介面,可以看出定時任務成功開啟

呼叫stopCorn介面,停止定時任務

補充

corn表示式的使用

cron 表示式是一個字串,該字串由 6 個空格分為 7 個域,每一個域代表一個時間含義。 通常定義 “年” 的部分可以省略,實際常用的 Cron 表示式由前 6 部分組成。格式如下:

[秒] [分] [時] [日] [月] [周] [年]
Seconds  Minutes  Hours   Day-of-Month  Month   Day-of-Week    Year (optional field)

需要說明的是,Cron 表示式中,“周” 是從週日開始計算的。“周” 域上的 1 表示的是週日,7 表示週六。

萬用字元說明

  • * 表示所有值. 例如:在分的欄位上設定 “*”,表示每一分鐘都會觸發
  • ? 表示不指定值。使用的場景為不需要關心當前設定這個欄位的值。例如:要在每月的10號觸發一個操作,但不關心是周幾,所以需要周位置的那個欄位設定為"?" 具體設定為 0 0 0 10 * ?
  • - 表示區間。例如 在小時上設定 “10-12”,表示 10,11,12點都會觸發。
  • , 表示指定多個值,例如在周欄位上設定 “MON,WED,FRI” 表示週一,週三和週五觸發
  • / 用於遞增觸發。如在秒上面設定"5/15" 表示從5秒開始,每增15秒觸發(5,20,35,50)。 在月欄位上設定’1/3’所示每月1號開始,每隔三天觸發一次。
  • L 表示最後的意思。在日欄位設定上,表示當月的最後一天(依據當前月份,如果是二月還會依據是否是潤年[leap]), 在周欄位上表示星期六,相當於"7"或"SAT"。如果在"L"前加上數字,則表示該資料的最後一個。例如在周欄位上設定"6L"這樣的格式,則表示“本月最後一個星期五"
  • W 表示離指定日期的最近那個工作日(週一至週五). 例如在日欄位上設定"15W",表示離每月15號最近的那個工作日觸發。如果15號正好是週六,則找最近的週五(14號)觸發, 如果15號是周未,則找最近的下週一(16號)觸發.如果15號正好在工作日(週一至週五),則就在該天觸發。如果指定格式為 “1W”,它則表示每月1號往後最近的工作日觸發。如果1號正是週六,則將在3號下週一觸發。(注,“W"前只能設定具體的數字,不允許區間”-").
  • # 序號(表示每月的第幾個周幾),例如在周欄位上設定"6#3"表示在每月的第三個週六.注意如果指定"#5",正好第五週沒有周六,則不會觸發該配置(用在母親節和父親節再合適不過了) ;

提示:
'L’和 'W’可以組合使用。如果在日欄位上設定"LW",則表示在本月的最後一個工作日觸發;
周欄位的設定,若使用英文字母是不區分大小寫的,即MON 與mon相同;

例子

  • "0 */1 * * * ?" 每隔 1 分鐘執行一次
  • "0 24,30 * * * ?" 在24分,30分執行一次
  • "0 0 10,14,16 * * ?" 每天上午10點,下午2點,4點
  • "0 0/30 9-17 * * ?" 朝九晚五工作時間內每半小時
  • "0 0 12 ? * WED" 表示每個星期三中午12點
  • "0 0 12 * * ?" 每天中午12點觸發
  • "0 15 10 ? * *" 每天上午10:15觸發
  • "0 15 10 * * ?" 每天上午10:15觸發
  • "0 15 10 * * ? *" 每天上午10:15觸發
  • "0 15 10 * * ? 2005" 2005年的每天上午10:15觸發
  • "0 * 14 * * ?" 在每天下午2點到下午2:59期間的每1分鐘觸發
  • "0 0/5 14 * * ?" 在每天下午2點到下午2:55期間的每5分鐘觸發
  • "0 0/5 14,18 * * ?" 在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發
  • "0 0-5 14 * * ?" 在每天下午2點到下午2:05期間的每1分鐘觸發
  • "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44觸發
  • "0 15 10 ? * MON-FRI" 週一至週五的上午10:15觸發
  • "0 15 10 15 * ?" 每月15日上午10:15觸發
  • "0 15 10 L * ?" 每月最後一日的上午10:15觸發
  • "0 15 10 ? * 6L" 每月的最後一個星期五上午10:15觸發
  • "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最後一個星期五上午10:15觸發
  • "0 15 10 ? * 6#3" 每月的第三個星期五上午10:15觸發