Java實(shí)現(xiàn)定時(shí)任務(wù)最簡單的3種方法
一、Timer
Timer是JAVA自帶的定時(shí)任務(wù)類,實(shí)現(xiàn)如下:
public class MyTimerTask {
public static void main(String[] args) {
// 定義一個(gè)任務(wù)
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("打印當(dāng)前時(shí)間:" + new Date());
}
};
// 計(jì)時(shí)器
Timer timer = new Timer();
// 開始執(zhí)行任務(wù) (延遲1000毫秒執(zhí)行,每3000毫秒執(zhí)行一次)
timer.schedule(timerTask, 1000, 3000);
}
}Timer 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn)是使用簡單,缺點(diǎn)是當(dāng)添加并執(zhí)行多個(gè)任務(wù)時(shí),前面任務(wù)的執(zhí)行用時(shí)和異常將影響到后面任務(wù),這邊深海建議謹(jǐn)慎使用。
二、ScheduledExecutorService
ScheduledExecutorService 也是Java自帶的類,
它可以實(shí)現(xiàn)Timer具備的所有功能,并解決了 Timer類存在的問題。
實(shí)現(xiàn)如下:
public class MyScheduledExecutorService {
public static void main(String[] args) {
// 創(chuàng)建任務(wù)隊(duì)列 10 為線程數(shù)量
ScheduledExecutorService scheduledExecutorService =
Executors.newScheduledThreadPool(10);
// 執(zhí)行任務(wù)
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("打印當(dāng)前時(shí)間:" + new Date());
}, 1, 3, TimeUnit.SECONDS); // 1s 后開始執(zhí)行,每 3s 執(zhí)行一次
}
}ScheduledExecutorService 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn)是,該類是JDK1.5自帶的類,使用簡單,缺點(diǎn)是該方案僅適用于單機(jī)環(huán)境。
三、Spring Task
Spring系列框架中Spring Framework自帶的定時(shí)任務(wù),
使用上面兩種方式,很難實(shí)現(xiàn)某些特定需求,比如每周一執(zhí)行某任務(wù),但SpringTask可輕松實(shí)現(xiàn)。
以SpringBoot為例來實(shí)現(xiàn):
1、開啟定時(shí)任務(wù)
在SpringBoot的啟動(dòng)類上聲明 @EnableScheduling:
@SpringBootApplication
@EnableScheduling //開啟定時(shí)任務(wù)
public class DemoApplication {
// -- --
}2、添加定時(shí)任務(wù)
只需使用@Scheduled注解標(biāo)注即可,
如果有多個(gè)定時(shí)任務(wù),可以創(chuàng)建多個(gè)@Scheduled標(biāo)注的方法,示例如下:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component // 把此類托管給 Spring,不能省略
public class TaskUtils {
// 添加定時(shí)任務(wù)
@Scheduled(cron = "30 40 23 0 0 5") // cron表達(dá)式:每周一 23:40:30 執(zhí)行
public void doTask(){
System.out.println("我是定時(shí)任務(wù)~");
}
}Spring Boot 啟動(dòng)后會(huì)自動(dòng)加載并執(zhí)行定時(shí)任務(wù),無需手動(dòng)操作。
Cron 表達(dá)式
Spring Task 的實(shí)現(xiàn)需要使用 cron 表達(dá)式來聲明執(zhí)行的頻率和規(guī)則,cron 表達(dá)式是由 6 位或者 7 位組成的(最后一位可以省略),每位之間以空格分隔,每位從左到右代表的含義如下:

其中 * 和 ? 號(hào)都表示匹配所有的時(shí)間。

cron 表達(dá)式在線生成地址:https://cron.qqe2.com/
知識(shí)擴(kuò)展:分布式定時(shí)任務(wù)
上面的方法都是關(guān)于單機(jī)定時(shí)任務(wù)的實(shí)現(xiàn),如果是分布式環(huán)境可以使用 Redis 來實(shí)現(xiàn)定時(shí)任務(wù)。
使用 Redis 實(shí)現(xiàn)延遲任務(wù)的方法大體可分為兩類:通過 ZSet 的方式和鍵空間通知的方式。
1、ZSet 實(shí)現(xiàn)方式
通過 ZSet 實(shí)現(xiàn)定時(shí)任務(wù)的思路是,將定時(shí)任務(wù)存放到 ZSet 集合中,并且將過期時(shí)間存儲(chǔ)到 ZSet 的 Score 字段中,然后通過一個(gè)無線循環(huán)來判斷當(dāng)前時(shí)間內(nèi)是否有需要執(zhí)行的定時(shí)任務(wù),如果有則進(jìn)行執(zhí)行,具體實(shí)現(xiàn)代碼如下:
import redis.clients.jedis.Jedis;
import utils.JedisUtils;
import java.time.Instant;
import java.util.Set;
public class DelayQueueExample {
private static final String _KEY = "DelayQueueExample";
public static void main(String[] args) throws InterruptedException {
Jedis jedis = JedisUtils.getJedis();
// 30s 后執(zhí)行
long delayTime = Instant.now().plusSeconds(30).getEpochSecond();
jedis.zadd(_KEY, delayTime, "order_1");
// 繼續(xù)添加測試數(shù)據(jù)
jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_2");
jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_3");
jedis.zadd(_KEY, Instant.now().plusSeconds(7).getEpochSecond(), "order_4");
jedis.zadd(_KEY, Instant.now().plusSeconds(10).getEpochSecond(), "order_5");
// 開啟定時(shí)任務(wù)隊(duì)列
doDelayQueue(jedis);
}
/**
* 定時(shí)任務(wù)隊(duì)列消費(fèi)
* @param jedis Redis 客戶端
*/
public static void doDelayQueue(Jedis jedis) throws InterruptedException {
while (true) {
// 當(dāng)前時(shí)間
Instant nowInstant = Instant.now();
long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond();
// 上一秒時(shí)間
long nowSecond = nowInstant.getEpochSecond();
// 查詢當(dāng)前時(shí)間的所有任務(wù)
Set data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond);
for (String item : data) {
// 消費(fèi)任務(wù)
System.out.println("消費(fèi):" + item);
}
// 刪除已經(jīng)執(zhí)行的任務(wù)
jedis.zremrangeByScore(_KEY, lastSecond, nowSecond);
Thread.sleep(1000); // 每秒查詢一次
}
}
}2、鍵空間通知
我們可以通過 Redis 的鍵空間通知來實(shí)現(xiàn)定時(shí)任務(wù),它的實(shí)現(xiàn)思路是給所有的定時(shí)任務(wù)設(shè)置一個(gè)過期時(shí)間,等到了過期之后,我們通過訂閱過期消息就能感知到定時(shí)任務(wù)需要被執(zhí)行了,此時(shí)我們執(zhí)行定時(shí)任務(wù)即可。
默認(rèn)情況下 Redis 是不開啟鍵空間通知的,需要我們通過 config set notify-keyspace-events Ex 的命令手動(dòng)開啟,開啟之后定時(shí)任務(wù)的代碼如下:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import utils.JedisUtils;
public class TaskExample {
public static final String _TOPIC = "__keyevent@0__:expired"; // 訂閱頻道名稱
public static void main(String[] args) {
Jedis jedis = JedisUtils.getJedis();
// 執(zhí)行定時(shí)任務(wù)
doTask(jedis);
}
/**
* 訂閱過期消息,執(zhí)行定時(shí)任務(wù)
* @param jedis Redis 客戶端
*/
public static void doTask(Jedis jedis) {
// 訂閱過期消息
jedis.psubscribe(new JedisPubSub() {
@Override
public void onPMessage(String pattern, String channel, String message) {
// 接收到消息,執(zhí)行定時(shí)任務(wù)
System.out.println("收到消息:" + message);
}
}, _TOPIC);
}
}總結(jié)
到此這篇關(guān)于Java實(shí)現(xiàn)定時(shí)任務(wù)最簡單的3種方法的文章就介紹到這了,更多相關(guān)Java定時(shí)任務(wù)實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決辦法
- Java實(shí)現(xiàn)定時(shí)任務(wù)的方法總結(jié)
- Java使用quartz實(shí)現(xiàn)定時(shí)任務(wù)示例詳解
- Java項(xiàng)目實(shí)現(xiàn)定時(shí)任務(wù)的三種方法
- java定時(shí)任務(wù)cron表達(dá)式每周執(zhí)行一次的坑及解決
- Java使用線程池執(zhí)行定時(shí)任務(wù)
- Java中定時(shí)任務(wù)的6種實(shí)現(xiàn)方式
- Java spring定時(shí)任務(wù)詳解
- Java 實(shí)現(xiàn)定時(shí)任務(wù)的三種方法
- java使用@Scheduled注解執(zhí)行定時(shí)任務(wù)
- Java定時(shí)任務(wù)取消的示例代碼
相關(guān)文章
Java?spring?通過注解方式創(chuàng)建對(duì)象的示例詳解
這篇文章主要介紹了java?spring?通過注解方式創(chuàng)建對(duì)象,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02
詳解Spring Cloud Hystrix斷路器實(shí)現(xiàn)容錯(cuò)和降級(jí)
本篇文章主要介紹了詳解Spring Cloud Hystrix斷路器實(shí)現(xiàn)容錯(cuò)和降級(jí),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
SpringBoot中的@ControllerAdvice使用方法詳細(xì)解析
這篇文章主要介紹了SpringBoot中的@ControllerAdvice使用方法詳細(xì)解析, 加了@ControllerAdvice的類為那些聲明了@ExceptionHandler、@InitBinder或@ModelAttribute注解修飾的 方法的類而提供的專業(yè)化的@Component,以供多個(gè) Controller類所共享,需要的朋友可以參考下2024-01-01
詳解MyBatis resultType與resultMap中的幾種返回類型
本文主要介紹了MyBatis resultType與resultMap中的幾種返回類型,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
細(xì)數(shù)java for循環(huán)中的那些坑
這篇文章主要介紹了Java for循環(huán)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07
Java如何實(shí)現(xiàn)壓縮文件與解壓縮zip文件
這篇文章主要介紹了Java如何實(shí)現(xiàn)壓縮文件與解壓縮zip文件問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12

