定時任務是每個業務常見的需求,比如每分鐘掃描逾時支付的訂單,每小時清理一次資料庫曆史資料,每天統計前一天的資料并生成報表等等。常見的解決方案有XXL-JOB、Spring-Task等。本篇文章着重于探讨Java 定時任務技術的發展曆程。
一、Timer
java.util.Timer 是JDK原生的工具類,用于建立定時任務。
建立 java.util.TimerTask 任務,在 run 方法中實作業務邏輯。通過 java.util.Timer 進行排程,支援按照固定頻率或指定Date時刻執行。所有的 TimerTask 是在同一個線程中串行執行,互相影響。也就是說,對于同一個 Timer 裡的多個 TimerTask 任務,如果一個 TimerTask 任務在執行中,其它 TimerTask 即使到達執行的時間,也隻能排隊等待。如果有異常産生,線程将退出,整個定時任務就失敗。
TimerTask timerTask1 = new TimerTask() {
@SneakyThrows
@Override
public void run() {
System.out.println("timerTask1 run ...");
Thread.sleep(10000);
System.out.println("timerTask1 finish ...");
}
};
TimerTask timerTask2 = new TimerTask() {
@Override
public void run() {
System.out.println(System.currentTimeMillis());
System.out.println("timerTask2 run ...");
}
};
Timer timer = new Timer();
DateTime date = DateUtil.parse("2022-08-04 15:30:00", DatePattern.NORM_DATETIME_PATTERN);
timer.schedule(timerTask1, date);
timer.schedule(timerTask2, 15000);
複制代碼
二、ScheduledExecutorService
ScheduledExecutorService 是 java.util.concurrent包下基于線程池設計的定時任務解決方案。
每個排程任務都會配置設定到線程池中的一個線程去執行,解決 Timer 定時器無法并發執行的問題,支援 fixedRate 和 fixedDelay。
public class ScheduledExecutorServiceTest {
private static final int CORE_SIZE = 5;
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(CORE_SIZE);
// 按照固定頻率執行,每隔5秒跑一次
executorService.scheduleAtFixedRate(() -> System.out.println("hello fixedRate"), 10, 5, TimeUnit.SECONDS);
// 按照固定延時執行,上次執行完後隔5秒執行下一次
executorService.scheduleWithFixedDelay(() -> System.out.println("hello fixedDelay"), 10, 5, TimeUnit.SECONDS);
}
}
複制代碼
三、Spring Task
Spring Task是 SpringBoot提供的輕量級定時任務工具。
我們通過注解可以很友善的配置,支援 cron 表達式、fixedRate、fixedDelay。
@Component
@EnableScheduling
public class SpringTaskTest {
/**
* 每分鐘的第30秒跑一次
*/
@Scheduled(cron = "30 * * * * ?")
public void task1() throws InterruptedException {
System.out.println("hello cron");
}
/**
* 每隔5秒跑一次
*/
@Scheduled(fixedRate = 5000)
public void task2() throws InterruptedException {
System.out.println("hello fixedRate");
}
/**
* 上次跑完隔3秒再跑
*/
@Scheduled(fixedDelay = 3000)
public void task3() throws InterruptedException {
System.out.println("hello fixedDelay");
}
}
複制代碼