背景
在日常的開發(fā)工作中我們經(jīng)常會(huì)遇到定時(shí)任務(wù)的相關(guān)問題,比如:
信用卡定時(shí)每月給用戶推送賬單數(shù)據(jù);
輪訓(xùn)更新某個(gè)任務(wù)的狀態(tài)是否完成;
設(shè)置一個(gè)定時(shí)提醒;
郵件或消息設(shè)置定時(shí)發(fā)送;
定時(shí)統(tǒng)計(jì)某個(gè)時(shí)間段的數(shù)據(jù)存入緩存;
一、基礎(chǔ)
什么是Crontab?
Crontab,即Cron Table(時(shí)間表)的簡稱,是一個(gè)在Linux和Unix系統(tǒng)中用于管理定時(shí)任務(wù)的調(diào)度器。它可以幫助我們在指定的時(shí)間周期性地執(zhí)行某些任務(wù),如定期備份數(shù)據(jù)、發(fā)送郵件提醒等。想象一下,Crontab就像一位貼心的秘書,在你需要的時(shí)間準(zhǔn)時(shí)執(zhí)行你交代的任務(wù)。
Crontab的工作原理
Crontab的工作原理非常簡單,它由一個(gè)名為“Cron”的守護(hù)進(jìn)程來負(fù)責(zé)定時(shí)執(zhí)行任務(wù)。當(dāng)你創(chuàng)建了一個(gè)Crontab任務(wù)時(shí),Cron守護(hù)進(jìn)程會(huì)按照你設(shè)定的時(shí)間規(guī)則來周期性地執(zhí)行這個(gè)任務(wù)。這個(gè)過程就像是你將一首歌設(shè)置為鬧鐘鈴聲,每天早上都會(huì)按時(shí)響起,叫醒你。
1.1 Cron表達(dá)式
cron是當(dāng)前做定時(shí)任務(wù)的基礎(chǔ),即使很多人說cron表達(dá)式不容易理解,但這是現(xiàn)在階段所有程序編寫定時(shí)任務(wù)的基礎(chǔ)和唯一選擇。
就算做不到熟練編寫,也應(yīng)該做到看到能懂;
Cron表達(dá)式是一個(gè)字符串,字符串以5或6個(gè)空格隔開,分為6或7個(gè)域,每一個(gè)域代表一個(gè)含義;
接下來具體看cron屬性對(duì)應(yīng)時(shí)間表達(dá)式的定義規(guī)則:
按順序依次是:秒、分、時(shí)、日、月、周,中間用空格間隔
月、周可以用數(shù)字或英文單詞的前三個(gè)字母表示
日和周可能會(huì)沖突,因此兩個(gè)可以有一個(gè)配置為?
常用通配符的含義:
表示任意值,例如在秒字段上設(shè)置,表示每秒都觸發(fā)
?表示不指定值,只能出現(xiàn)在日或者周的位置,用于處理日和周可能存在的沖突,例如2020年8月15是周六,如果又在周的位置上指定為周一,那就會(huì)產(chǎn)生沖突到導(dǎo)致定時(shí)任務(wù)失效。如果我們不關(guān)心日或者周的時(shí)候,也可以將其設(shè)置為?
-表示時(shí)間區(qū)間,例如在秒上設(shè)置1-3,表示第1、2、3秒都會(huì)觸發(fā)
/表示時(shí)間間隔,例如在秒上設(shè)置2/4,表示從第2秒開始每間隔4秒觸發(fā)一次
,表示列舉多個(gè)值,例如MON,WED,FRI表示周一、周三、周五觸發(fā)
例如:
每隔5秒執(zhí)行一次:*/5 * * * * ?
每隔1分鐘執(zhí)行一次:0 */1 * * * ?
每天23點(diǎn)執(zhí)行一次:0 0 23 * * ?
每天凌晨1點(diǎn)執(zhí)行一次:0 0 1 * * ?
每月1號(hào)凌晨1點(diǎn)執(zhí)行一次:0 0 1 1 * ?
每月最后一天23點(diǎn)執(zhí)行一次:0 0 23 L * ?
每周星期天凌晨1點(diǎn)實(shí)行一次:0 0 1 ? * L
在26分、29分、33分執(zhí)行一次:0 26,29,33 * * * ?
每天的0點(diǎn)、13點(diǎn)、18點(diǎn)、21點(diǎn)都執(zhí)行一次:0 0 0,13,18,21 * * ?
1.2 定時(shí)任務(wù)的三大組成部分
調(diào)度器Scheduler、執(zhí)行器 executors、觸發(fā)器 Trigger
不管你使用的什么框架、用的是什么系統(tǒng),或者將來又出現(xiàn)什么新的定時(shí)任務(wù)技術(shù),都離不開這三部分。
我們以一個(gè)鬧鐘響鈴的任務(wù)為例:
執(zhí)行器executors:發(fā)出一陣刺耳的聲音;(具體的執(zhí)行操作)
觸發(fā)器Trigger:發(fā)出聲音的具體時(shí)間; (觸發(fā)任務(wù)執(zhí)行的規(guī)則,多為時(shí)間規(guī)則。)
調(diào)度器Scheduler:一直運(yùn)行到觸發(fā)時(shí)間點(diǎn)發(fā)出刺耳的聲音;(進(jìn)行任務(wù)的調(diào)度)
所以,當(dāng)接手一個(gè)新的定時(shí)任務(wù)的框架,首先要看其這三部分是這么去實(shí)現(xiàn)的;
二、Java做定時(shí)任務(wù)的技術(shù)方案比較
Java做定時(shí)任務(wù)的技術(shù)方案有多種,本文將比較常見的技術(shù)方案,分析它們的優(yōu)缺點(diǎn),以便開發(fā)人員在實(shí)際項(xiàng)目中選擇合適的技術(shù)方案。
2.1、JDK seelp實(shí)現(xiàn)定時(shí)任務(wù)
使用Thread.sleep()方法實(shí)現(xiàn)定時(shí)任務(wù)??梢栽讵?dú)立的線程中執(zhí)行任務(wù),并在每次任務(wù)完成后使用Thread.sleep()方法讓線程休眠一段時(shí)間。
下面是一個(gè)示例,該示例定義了一個(gè)任務(wù),該任務(wù)每隔1秒打印“執(zhí)行任務(wù)每秒一次”:
public class TimerExample {
public static void main(String[] args) {
Runnable task = new Runnable() {
public void run() {
while (true) {
System.out.println("執(zhí)行任務(wù)每秒一次");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(task);
thread.start();
}
}
請注意,在本例中,任務(wù)是在無限循環(huán)中執(zhí)行的,并且每次任務(wù)完成后線程都會(huì)休眠1秒。如果想要執(zhí)行特定次數(shù)的定時(shí)任務(wù),可以在任務(wù)內(nèi)部使用計(jì)數(shù)器并在達(dá)到特定次數(shù)后終止循環(huán)。
2.2、JDK Timer & TimerTask 實(shí)現(xiàn)定時(shí)任務(wù)
使用Java的java.util.Timer和java.util.TimerTask類來創(chuàng)建定時(shí)任務(wù)。
下面是一個(gè)簡單的例子,其中定義了一個(gè)任務(wù),該任務(wù)每隔1秒執(zhí)行一次:
import java.util.Timer;
import java.util.TimerTask;
public class TimerExample {
public static void main(String[] args) {
TimerTask task = new TimerTask() {
public void run() {
System.out.println("執(zhí)行任務(wù)每秒一次");
} };
Timer timer = new Timer();
long delay = 1000;
long interval = 1000;
timer.scheduleAtFixedRate(task, delay, interval);
}
}
在上面的代碼中,創(chuàng)建了一個(gè)TimerTask對(duì)象,該對(duì)象的run方法定義了要執(zhí)行的任務(wù)。然后,使用Timer類創(chuàng)建了一個(gè)定時(shí)器,并使用scheduleAtFixedRate方法指定了任務(wù)的執(zhí)行頻率(即每隔1秒執(zhí)行一次)。
請注意,java.util.Timer是非線程安全的,因此在多線程環(huán)境中使用時(shí)需要注意。
如果需要更復(fù)雜的定時(shí)任務(wù),請考慮使用其他庫,例如java.util.concurrent中的ScheduledExecutorService。
在Java 5之前,它是唯一的定時(shí)任務(wù)工具,在Java 5之后,JDK提供了更好的替代方案。
優(yōu)點(diǎn):
輕量級(jí),使用簡單。
可以實(shí)現(xiàn)較為簡單的定時(shí)任務(wù)功能。
缺點(diǎn):
不支持復(fù)雜的定時(shí)任務(wù)類型,如cron表達(dá)式
不支持任務(wù)持久化
在定時(shí)任務(wù)數(shù)量很多的情況下,性能可能存在問題
不支持分布式
2.3、JDK ScheduledExecutorService
ScheduledExecutorService是Java 5新增的一個(gè)接口,它位于java.util.concurrent包內(nèi),可以通過ThreadPoolExecutor來實(shí)現(xiàn)。可以使用它創(chuàng)建和執(zhí)行定期執(zhí)行的任務(wù),并且可以控制任務(wù)的執(zhí)行頻率。
它可以很方便地實(shí)現(xiàn)定時(shí)任務(wù),支持多種執(zhí)行方式,比如單次定時(shí)、固定頻率定時(shí)和固定延遲時(shí)間定時(shí)等。
以下是一個(gè)示例,該示例每隔1秒鐘打印“執(zhí)行任務(wù)每秒一次”:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorServiceExample {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable task = new Runnable() {
public void run() {
System.out.println("執(zhí)行任務(wù)每秒一次");
}
};
executor.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);
}
}
在本例中,使用Executors類的newScheduledThreadPool方法創(chuàng)建了一個(gè)ScheduledExecutorService對(duì)象,并使用該對(duì)象的scheduleAtFixedRate方法將任務(wù)安排在每隔1秒鐘執(zhí)行一次。可以根據(jù)需要更改任務(wù)的執(zhí)行頻率。
優(yōu)點(diǎn):
簡單易用,可靈活配置。
支持多種定時(shí)任務(wù)類型,靈活度高。
支持并發(fā)調(diào)度,可以提高性能。
缺點(diǎn):
不支持分布式
任務(wù)執(zhí)行結(jié)果管理不夠方便
2.4、Quartz框架
Quartz是一個(gè)流行的開源的定時(shí)任務(wù)框架,支持非常豐富的定時(shí)任務(wù)類型,比如簡單定時(shí)任務(wù)、復(fù)雜定時(shí)任務(wù)、集群定時(shí)任務(wù)、任務(wù)持久化等。
以下是一個(gè)使用Quartz創(chuàng)建定時(shí)任務(wù)的示例:
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzExample {
public static void main(String[] args) throws SchedulerException {
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("myJob", "group1")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
}
}
public class HelloJob implements Job {
public void execute(JobExecutionContext context) {
System.out.println("執(zhí)行任務(wù)每秒一次");
}
}
在本例中,創(chuàng)建了一個(gè)名為HelloJob的作業(yè)類,該類實(shí)現(xiàn)了Quartz的Job接口。
還創(chuàng)建了一個(gè)作業(yè)詳細(xì)信息對(duì)象和一個(gè)觸發(fā)器對(duì)象,并使用觸發(fā)器安排了每隔1秒鐘執(zhí)行一次的任務(wù)。最后,啟動(dòng)了調(diào)度程序并使用其scheduleJob方法將任務(wù)與觸發(fā)器關(guān)聯(lián)。
優(yōu)點(diǎn):
功能豐富,支持多種定時(shí)任務(wù)類型。
支持任務(wù)持久化,保證任務(wù)不會(huì)丟失。
支持分布式,可以實(shí)現(xiàn)任務(wù)的高可用和負(fù)載均衡。
缺點(diǎn):
依賴比較大,使用起來比較復(fù)雜。
配置較為繁瑣,需要考慮集群和持久化等問題。
2.5、Spring Task 中的 @schedule
Spring Task是Spring框架提供的一個(gè)模塊,可以用來進(jìn)行定時(shí)任務(wù)的管理。通過@Scheduled注解,可以實(shí)現(xiàn)簡單的定時(shí)任務(wù)設(shè)置。同時(shí)也支持通過cron表達(dá)式設(shè)置復(fù)雜的定時(shí)任務(wù)。
以下是一個(gè)示例,該示例每隔1秒鐘打印“執(zhí)行任務(wù)每秒一次”:
import org.springframework.scheduling.annotation.Scheduled;
public class ScheduledAnnotationExample {
@Scheduled(fixedRate = 1000)
public void printMessage() {
System.out.println("執(zhí)行任務(wù)每秒一次");
}
}
在本例中,使用@Scheduled注解將任務(wù)標(biāo)記為定時(shí)任務(wù),并使用fixedRate屬性設(shè)置任務(wù)的執(zhí)行頻率,即每隔1秒鐘執(zhí)行一次。
優(yōu)點(diǎn):
集成簡單,開發(fā)便捷。
支持使用注解來配置定時(shí)任務(wù),使得代碼更加易讀易維護(hù)。
缺點(diǎn):
不支持分布式
不支持任務(wù)持久化
2.6、Elastic-Job框架
Elastic-Job是一個(gè)分布式作業(yè)調(diào)度框架,提供了高可用性、高擴(kuò)展性等功能,可以用于創(chuàng)建分布式定時(shí)任務(wù)。
以下是使用Elastic-Job創(chuàng)建一個(gè)簡單的定時(shí)任務(wù)的示例:
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
public class MyJob implements SimpleJob {
@Override public void execute(ShardingContext shardingContext) {
System.out.println("執(zhí)行任務(wù)每秒一次");
}
}
在本例中,創(chuàng)建了一個(gè)名為MyJob的作業(yè)類,該類實(shí)現(xiàn)了Elastic-Job的SimpleJob接口??梢栽趀xecute方法中編寫定時(shí)執(zhí)行的代碼。還需要?jiǎng)?chuàng)建一個(gè)作業(yè)配置文件,該文件配置了任務(wù)的定時(shí)時(shí)間和任務(wù)的執(zhí)行策略。
2.7、PowerJob框架
PowerJob是一種使用Java語言編寫的分布式任務(wù)調(diào)度系統(tǒng),可以用于創(chuàng)建定時(shí)任務(wù)。
以下是使用PowerJob創(chuàng)建一個(gè)簡單的定時(shí)任務(wù)的示例:
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyJob implements SimpleJob {
private static final Logger logger = LoggerFactory.getLogger(MyJob.class);
@Override
public void execute(ShardingContext shardingContext) {
logger.info("執(zhí)行任務(wù)每秒一次");
}
}
在本例中,創(chuàng)建了一個(gè)名為MyJob的簡單作業(yè)類,該類實(shí)現(xiàn)了PowerJob的SimpleJob接口??梢栽趀xecute方法中編寫定時(shí)執(zhí)行的代碼。
2.8、Xxl-job
Xxl-job是一個(gè)Java分布式定時(shí)任務(wù)解決方案,提供了高可用性、高擴(kuò)展性等功能,可以用于創(chuàng)建分布式定時(shí)任務(wù)??梢杂脕泶鍽uartz。它支持類似cron表達(dá)式的定時(shí)任務(wù)設(shè)置,支持多種執(zhí)行器類型,比如本地任務(wù)、Shell任務(wù)、Http任務(wù)等。同時(shí)還支持任務(wù)執(zhí)行結(jié)果的管理和調(diào)度中心的高可用等特性。
以下是使用XXL-JOB創(chuàng)建一個(gè)簡單的定時(shí)任務(wù)的示例:
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobLogger;
import org.springframework.stereotype.Component;
@JobHandler(value="myJobHandler")
@Component
public class MyJobHandler extends IJobHandler {
@Override
public ReturnT<String> execute(String param) throws Exception {
XxlJobLogger.log("執(zhí)行任務(wù)每秒一次");
return SUCCESS;
}
}
在本例中,創(chuàng)建了一個(gè)名為MyJobHandler的作業(yè)類,該類繼承了XXL-JOB的IJobHandler類??梢栽趀xecute方法中編寫定時(shí)執(zhí)行的代碼。
還需要通過注解@JobHandler聲明作業(yè)處理器名稱,并在作業(yè)配置中將作業(yè)處理器與作業(yè)關(guān)聯(lián)。
優(yōu)點(diǎn):
操作簡單,集成容易。
多種任務(wù)執(zhí)行器類型,支持更多的任務(wù)類型。
支持可視化的調(diào)度中心,調(diào)度管理更加方便。
缺點(diǎn):
比較適合定時(shí)任務(wù)數(shù)量較多,任務(wù)類型較為豐富的情況,對(duì)于簡單的定時(shí)任務(wù)使用稍顯繁瑣。文章來源:http://www.zghlxwxcb.cn/news/detail-764718.html
三、共同點(diǎn)和不同點(diǎn)文章來源地址http://www.zghlxwxcb.cn/news/detail-764718.html
到了這里,關(guān)于【Java 定時(shí)任務(wù)】crontab定時(shí)任務(wù)配置(139)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!