Java單機(jī)環(huán)境實(shí)現(xiàn)定時(shí)任務(wù)技術(shù)
前言
開(kāi)發(fā)的系統(tǒng)中過(guò)程,會(huì)有如下的需求:
- 凌晨生成訂單統(tǒng)計(jì)報(bào)表
- 定時(shí)推送文章、發(fā)送郵件
- 每隔5分鐘動(dòng)態(tài)抓取數(shù)據(jù)更新
- 每晚定時(shí)計(jì)算用戶當(dāng)日收益情況并推送給用戶
- .....
通常這些需求我們都是通過(guò)定時(shí)任務(wù)來(lái)實(shí)現(xiàn),列舉了java中一些常用的的定時(shí)任務(wù)框架。
定時(shí)任務(wù)框架
TimeTask
從我們開(kāi)始學(xué)習(xí)java開(kāi)始,最先實(shí)現(xiàn)定時(shí)任務(wù)的時(shí)候都是采用TimeTask, Timer內(nèi)部使用TaskQueue的類(lèi)存放定時(shí)任務(wù),它是一個(gè)基于最小堆實(shí)現(xiàn)的優(yōu)先級(jí)隊(duì)列。TaskQueue會(huì)按照任務(wù)距離下一次執(zhí)行時(shí)間的大小將任務(wù)排序,保證在堆頂?shù)娜蝿?wù)最先執(zhí)行。
實(shí)例代碼:
public static void main(String[] args) { TimerTask task = new TimerTask() { public void run() { System.out.println("當(dāng)前時(shí)間: " + new Date() + "n" + "線程名稱(chēng): " + Thread.currentThread().getName()); } }; Timer timer = new Timer("Timer"); long delay = 5000L; timer.schedule(task, delay); System.out.println("當(dāng)前時(shí)間: " + new Date() + "n" + "線程名稱(chēng): " + Thread.currentThread().getName()); }
運(yùn)行結(jié)果:
當(dāng)前時(shí)間: Wed Apr 06 22:05:04 CST 2022n線程名稱(chēng): main 當(dāng)前時(shí)間: Wed Apr 06 22:05:09 CST 2022n線程名稱(chēng): Timer 復(fù)制代碼
從結(jié)果可以看出,5秒后執(zhí)行了定時(shí)任務(wù)。
缺點(diǎn):
- TimeTask執(zhí)行任務(wù)只能串行執(zhí)行,一旦一個(gè)任務(wù)執(zhí)行的時(shí)間比較長(zhǎng)的話將會(huì)影響其他任務(wù)執(zhí)行
- 執(zhí)行任務(wù)過(guò)程如果發(fā)生異常,任務(wù)會(huì)直接停止。
隨著時(shí)間的推移,java的技術(shù)也在不斷的更新,針對(duì)TimeTask的不足,ScheduledExecutorService出現(xiàn)替代了TimeTask。
ScheduledExecutorService
ScheduledExecutorService是一個(gè)接口,有多個(gè)實(shí)現(xiàn)類(lèi),比較常用的是ScheduledThreadPoolExecutor。而ScheduledThreadPoolExecutor本身就是一個(gè)線程池,其內(nèi)部使用 DelayQueue 作為任務(wù)隊(duì)列,并且支持任務(wù)并發(fā)執(zhí)行。
實(shí)例代碼:
public static void main(String[] args) throws InterruptedException { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3); // 執(zhí)行任務(wù): 每 10秒執(zhí)行一次 executorService.scheduleAtFixedRate(() -> { System.out.println("執(zhí)行任務(wù):" + new Date()+",線程名稱(chēng): " + Thread.currentThread().getName()); }, 1, 10, TimeUnit.SECONDS); }
缺點(diǎn):
- 盡量避免用Executors方式去創(chuàng)建線程池,因?yàn)閖dk自帶線程池內(nèi)部使用的的隊(duì)列的比較大,很容易出現(xiàn)OOM。
- 定時(shí)任務(wù)是基于JVM單機(jī)內(nèi)存形式的,一旦重啟定時(shí)任務(wù)就消失了。
- 無(wú)法支持cron表達(dá)式實(shí)現(xiàn)豐富的定時(shí)任務(wù)。
Spring Task
學(xué)習(xí)了Spring之后,開(kāi)始使用了Spring 自帶的Task。Spring Framework 自帶定時(shí)任務(wù),提供了 cron 表達(dá)式來(lái)實(shí)現(xiàn)豐富定時(shí)任務(wù)配置。
實(shí)例代碼:
@EnableScheduling @Component public class SpringTask { private Logger logger = LoggerFactory.getLogger(SpringTask.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "HH:mm:ss"); /** * fixedRate:固定速率執(zhí)行。每5秒執(zhí)行一次。 */ @Scheduled(fixedRate = 5000) public void invokeTaskWithFixedRate() { logger.info("Fixed Rate Task : Current Time is {}", dateFormat.format(new Date())); } /** * fixedDelay:固定延遲執(zhí)行。距離上一次調(diào)用成功后2秒才執(zhí)。 */ @Scheduled(fixedDelay = 2000) public void invokeTaskWithFixedDelay() { try { TimeUnit.SECONDS.sleep(3); logger.info("Fixed Delay Task : Current Time is {}", dateFormat.format(new Date())); } catch (InterruptedException e) { logger.error("invoke task error",e); } } /** * initialDelay:初始延遲。任務(wù)的第一次執(zhí)行將延遲5秒,然后將以5秒的固定間隔執(zhí)行。 */ @Scheduled(initialDelay = 5000, fixedRate = 5000) public void invokeTaskWithInitialDelay() { logger.info("Task with Initial Delay : Current Time is {}", dateFormat.format(new Date())); } /** * cron:使用Cron表達(dá)式,每隔5秒執(zhí)行一次 */ @Scheduled(cron = "0/5 * * * * ? ") public void invokeTaskWithCronExpression() { logger.info("Task Cron Expression: Current Time is {}", dateFormat.format(new Date())); } }
執(zhí)行結(jié)果:
2022-04-06 23:06:20.945 INFO 14604 --- [ scheduling-1] com.fw.task.SpringTask : Task Cron Expression: Current Time is 23:06:20
2022-04-06 23:06:22.557 INFO 14604 --- [ scheduling-1] com.fw.task.SpringTask : Task with Initial Delay : Current Time is 23:06:22
2022-04-06 23:06:22.557 INFO 14604 --- [ scheduling-1] com.fw.task.SpringTask : Fixed Rate Task : Current Time is 23:06:22
2022-04-06 23:06:25.955 INFO 14604 --- [ scheduling-1] com.fw.task.SpringTask : Fixed Delay Task : Current Time is 23:06:25
2022-04-06 23:06:25.955 INFO 14604 --- [ scheduling-1] com.fw.task.SpringTask : Task Cron Expression: Current Time is 23:06:25
2022-04-06 23:06:27.555 INFO 14604 --- [ scheduling-1] com.fw.task.SpringTask : Task with Initial Delay : Current Time is 23:06:27
2022-04-06 23:06:27.556 INFO 14604 --- [ scheduling-1] com.fw.task.SpringTask : Fixed Rate Task : Current Time is 23:06:27
@EnableScheduling需要開(kāi)啟定時(shí)任務(wù),@Scheduled(cron = "0/5 * * * * ?")配置定時(shí)任務(wù)的規(guī)則。cron表達(dá)式支持豐富定時(shí)任務(wù)配置,不熟悉的的可以查看
優(yōu)點(diǎn):
使用簡(jiǎn)單方便,支持各種復(fù)雜的定時(shí)任務(wù)配置
缺點(diǎn):
- 基于單機(jī)形式定時(shí)任務(wù),一旦重啟定時(shí)任務(wù)就消失了。
- 定時(shí)任務(wù)默認(rèn)是單線程執(zhí)行任務(wù),如果需要并行執(zhí)行需要開(kāi)啟@EnableAsync。
- 沒(méi)有統(tǒng)一的圖形化任務(wù)調(diào)度的管理,無(wú)法控制定時(shí)任務(wù)
結(jié)語(yǔ)
任何技術(shù)的出現(xiàn)都有它的價(jià)值,技術(shù)沒(méi)有最好的,只有最合適的。我們要針對(duì)不同的需求選擇最合適的技術(shù),切莫過(guò)度架構(gòu)設(shè)計(jì)。本文講解了單機(jī)環(huán)境實(shí)現(xiàn)定時(shí)任務(wù)的技術(shù),
到此這篇關(guān)于Java單機(jī)環(huán)境實(shí)現(xiàn)定時(shí)任務(wù)技術(shù)的文章就介紹到這了,更多相關(guān)Java定時(shí)任務(wù)框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 根據(jù)貸款年限對(duì)應(yīng)利率計(jì)算功能實(shí)現(xiàn)解析
這篇文章主要介紹了Java 根據(jù)貸款年限對(duì)應(yīng)利率計(jì)算功能實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Java多線程實(shí)現(xiàn)之Executor詳解
這篇文章主要介紹了Java多線程實(shí)現(xiàn)之Executor詳解,Executor 給他一個(gè) Runnable,他就能自動(dòng)很安全的幫你把這個(gè)線程執(zhí)行完畢2023-08-08
Executor 通過(guò)創(chuàng)建線程池的方式來(lái)管理線程,需要的朋友可以參考下SpringBoot實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04SpringBoot中controller深層詳細(xì)講解
這篇文章主要介紹了SpringBoot在Controller層接收參數(shù)的常用方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02java 使用JDBC構(gòu)建簡(jiǎn)單的數(shù)據(jù)訪問(wèn)層實(shí)例詳解
以下是如何使用JDBC構(gòu)建一個(gè)數(shù)據(jù)訪問(wèn)層,包括數(shù)據(jù)轉(zhuǎn)換(將從數(shù)據(jù)庫(kù)中查詢(xún)的數(shù)據(jù)封裝到對(duì)應(yīng)的對(duì)象中……),數(shù)據(jù)庫(kù)的建立,以及如何連接到數(shù)據(jù)庫(kù),需要的朋友可以參考下2016-11-11Idea2023配置JavaWeb項(xiàng)目(最新)
本文將介紹如何配置JavaWeb項(xiàng)目,以在Idea中實(shí)現(xiàn)開(kāi)發(fā)環(huán)境,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09Java實(shí)現(xiàn)獲取小程序帶參二維碼并保存到本地
這篇文章主要介紹了Java實(shí)現(xiàn)獲取小程序帶參二維碼并保存到本地,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10