亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java定時任務(wù)的三種實(shí)現(xiàn)方式

 更新時間:2019年01月28日 08:39:55   作者:Single_Yam  
這篇文章主要給大家介紹了關(guān)于Java定時任務(wù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

現(xiàn)代的應(yīng)用程序早已不是以前的那些由簡單的增刪改查拼湊而成的程序了,高復(fù)雜性早已是標(biāo)配,而任務(wù)的定時調(diào)度與執(zhí)行也是對程序的基本要求了。

很多業(yè)務(wù)需求的實(shí)現(xiàn)都離不開定時任務(wù),例如,每月一號,移動將清空你上月未用完流量,重置套餐流量,以及備忘錄提醒、鬧鐘等功能。

Java 系統(tǒng)中主要有三種方式來實(shí)現(xiàn)定時任務(wù):

  • Timer和TimerTask
  • ScheduledExecutorService
  • 三方框架 Quartz

下面我們一個個來看。

Timer和TimerTask

先看一個小 demo,接著我們再來分析其中原理:

這種方式的定時任務(wù)主要用到兩個類,Timer 和 TimerTask。其中,TimerTask 繼承接口 Runnable,抽象的描述一種任務(wù)類型,我們只要重寫實(shí)現(xiàn)它的 run 方法就可以實(shí)現(xiàn)自定義任務(wù)。

而 Timer 就是用于定時任務(wù)調(diào)度的核心類,demo 中我們調(diào)用其 schedule 并指定延時 1000 毫秒,所以上述代碼會在一秒鐘后完成打印操作,接著程序結(jié)束。

那么,使用上很簡單,兩個步驟即可,但是其中的實(shí)現(xiàn)邏輯是怎樣的呢?

Timer 接口

首先,Timer 接口中,這兩個字段是非常核心重要的:

TaskQueue 是一個隊(duì)列,內(nèi)部由動態(tài)數(shù)組實(shí)現(xiàn)的最小堆結(jié)構(gòu),換句話說,它是一個優(yōu)先級隊(duì)列。而優(yōu)先級參考下一次執(zhí)行時間,越快執(zhí)行的越排在前面,這一點(diǎn)我們回頭再研究。

接著,這個 TimerThread 類其實(shí)是 Timer 的一個內(nèi)部類,它繼承了 Thread 并重寫了其 run 方法,該線程實(shí)例將在構(gòu)建 Timer 實(shí)例的時候被啟動。

run 方法內(nèi)部會循環(huán)的從隊(duì)列中取任務(wù),如果沒有就阻塞自己,而當(dāng)我們成功的向隊(duì)列中添加了定時任務(wù),也會嘗試喚醒該線程。

我們也來看一下 Timer 的構(gòu)造方法:

public Timer(String name) {
 thread.setName(name);
 thread.start();
}

再簡單不過的構(gòu)造函數(shù)了,為內(nèi)部線程設(shè)置線程名,并啟動該線程。

最后,我們著重看一下 Timer 中用于配置一個定時任務(wù)進(jìn)任務(wù)隊(duì)列的方法。

//在時刻 time 處執(zhí)行任務(wù)
schedule(TimerTask task, Date time)

//延時 delay 毫秒后執(zhí)行任務(wù)
schedule(TimerTask task, long delay)

//固定延時重復(fù)執(zhí)行,firstTime為首次執(zhí)行時間,
//往后沒間隔 period 毫秒執(zhí)行一次
schedule(TimerTask task, Date firstTime, long period)

//固定延時重復(fù)執(zhí)行
//首次執(zhí)行時間為當(dāng)前時間延時 delay 毫秒
schedule(TimerTask task, long delay, long period)

//固定頻率重復(fù)執(zhí)行,每過 period 毫秒執(zhí)行一次
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

//固定頻率重復(fù)執(zhí)行
scheduleAtFixedRate(TimerTask task, long delay, long period)

相信有了注釋,這幾個方法的區(qū)別與作用應(yīng)該不難理解,但是其中有兩個概念需要作一點(diǎn)區(qū)分。

==固定延時== VS ==固定頻率==

固定延時:以任務(wù)的上一次 實(shí)際 執(zhí)行時間做參考,往后延時 period 毫秒。

固定頻率:任務(wù)的往后每一次執(zhí)行時間都在任務(wù)提交的那一刻得到了確定,不論你上次任務(wù)是否意外延時了,定時定點(diǎn)執(zhí)行下一次任務(wù)。

這兩者的區(qū)別還是很大的,希望你能夠理解清楚,接著我們以其中一個方法為例,看看底層實(shí)現(xiàn)。

以這個方法為例,其他重載方法的底層調(diào)用都是同樣的,我們不去贅述。

這個方法的作用,我們再說一遍。

以當(dāng)前時間為準(zhǔn),延時 delay 毫秒后第一次執(zhí)行該任務(wù),并且采取固定延時的方式,每隔 period 毫秒再次執(zhí)行該任務(wù)。

開頭的兩個異常判斷我們不再贅述,看看 sched 方法:

方法需要傳入三個參數(shù),參數(shù) task 代表的需要執(zhí)行的任務(wù)體,TimerTask 我們回頭會詳細(xì)介紹,這里你知道它代表了一個任務(wù)體即可。

參數(shù) time 描述了該任務(wù)下一次執(zhí)行的時刻,計(jì)算機(jī)底層是以毫秒描述時刻的,所以這里轉(zhuǎn)換為 long 類型來描述時刻。

參數(shù) period 是固定延時的毫秒數(shù)。

整個方法的邏輯我們可以總結(jié)概括一下,具體的代碼就不一行行分析了,因?yàn)橐膊浑y。

  1. 首先使用任務(wù)隊(duì)列的內(nèi)置對象鎖,鎖住個隊(duì)列。
  2. 接著再去鎖住我們的 task,并修改其內(nèi)部的一些屬性字段值,nextExecutionTime 指明下一次任務(wù)執(zhí)行時間,period 設(shè)置固定延時的毫秒數(shù),修改 state 狀態(tài)為計(jì)劃中。
  3. 然后將 task 添加到任務(wù)隊(duì)列,其中 add 方法內(nèi)部會進(jìn)行最小堆重構(gòu),參考的就是 nextExecutionTime 字段的值,越小優(yōu)先級越高。
  4. 判斷如果自己就是隊(duì)列第一個任務(wù),那么將喚醒 Timer 中阻塞了的任務(wù)線程。

可能會有人疑問,Timer 如何判斷一個任務(wù)是否是重復(fù)執(zhí)行的,還是單次執(zhí)行就結(jié)束的?

答案在 TimerThread 的 run 方法里,有興趣你可以去研究下,方法體比較多比較長,這里不做分析。

當(dāng)我們構(gòu)造 Timer 實(shí)例的時候,就會啟動該線程,該線程會在一個死循環(huán)中嘗試從任務(wù)隊(duì)列上獲取任務(wù),如果成功獲取就執(zhí)行該任務(wù)并在執(zhí)行結(jié)束之后做一個判斷。

如果 period 值為零,則說明這是一次普通任務(wù),執(zhí)行結(jié)束后將從隊(duì)列首部移除該任務(wù)。

如果 period 為負(fù)值,則說明這是一次固定延時的任務(wù),修改它下次執(zhí)行時間 nextExecutionTime 為當(dāng)前時間減去 period,重構(gòu)任務(wù)隊(duì)列。

如果 period 為正數(shù),則說明這是一次固定頻率的任務(wù),修改它下次執(zhí)行時間為 上次執(zhí)行時間加上 period,并重構(gòu)任務(wù)隊(duì)列。

其實(shí),我也已經(jīng)把 TimerThread 的 run 方法里最核心的邏輯也已經(jīng)介紹了,建議大家親自去研究研究具體代碼的實(shí)現(xiàn),你會對這一塊的邏輯更清晰。

最后,我們看一看這個 Timer 它有哪些劣勢的地方:

  • Timer 的背后只有一個線程,不管你有多少個任務(wù),都只有一個工作線程,效率上必然是要打折扣的。
  • 限于單線程,如果第一個任務(wù)邏輯上死循環(huán)了,后續(xù)的任務(wù)一個都得不到執(zhí)行。
  • 依然是由于單線程,任一任務(wù)拋出異常后,整個 Timer 就會結(jié)束,后續(xù)任務(wù)全部都無法執(zhí)行。

所以你看,單線程的 Timer 帶來了太多局限性,于是我們看它的替代者。

PS:本來計(jì)劃再介紹下 TimerTask 這個抽象任務(wù)類的,但是發(fā)現(xiàn)實(shí)在沒啥好介紹的,就是增加了兩個字段,一個用于記錄下一次該任務(wù)的執(zhí)行時間,一個用于延時毫秒數(shù)。你也只需要重寫其 run 方法即可。

ScheduledExecutorService

這個接口相信你一定眼熟,我告訴你在哪見過。

你看,它是我們異步框架中的接口,正好我們今天來介紹他,這樣整個異步框架中所有的接口我們都分析過了。

ScheduledExecutorService中定義的這四個接口方法和 Timer 中對應(yīng)的方法幾乎一樣,只不過 Timer 的 scheduled 方法需要在外部傳入一個 TimerTask 的抽象任務(wù)。

而我們的 ScheduledExecutorService 封裝的更加細(xì)致了,隨便你傳 Runnable 或是 Callable,我會在內(nèi)部給你做一層封裝,封裝一個類似 TimerTask 的抽象任務(wù)類(ScheduledFutureTask)。

然后傳入線程池,啟動線程去執(zhí)行該任務(wù),而我們的 ScheduledFutureTask 重寫的 run 方法是這樣的:

如果 periodic 為 true 則說明這是一個需要重復(fù)執(zhí)行的任務(wù),否則說明是一個一次性任務(wù)。

所以實(shí)際執(zhí)行該任務(wù)的時候,需要分類,如果是普通的任務(wù)就直接調(diào)用 run 方法執(zhí)行即可,否則在執(zhí)行結(jié)束之后還需要重置下下一次執(zhí)行時間。

整體來說,ScheduledExecutorService 區(qū)別于 Timer 的地方就在于前者依賴了線程池來執(zhí)行任務(wù),而任務(wù)本身會判斷是什么類型的任務(wù),需要重復(fù)執(zhí)行的在任務(wù)執(zhí)行結(jié)束后會被重新添加到任務(wù)隊(duì)列。

而對于后者來說,它只依賴一個線程不停的去獲取隊(duì)列首部的任務(wù)并嘗試執(zhí)行它,無論是效率上、還是安全性上都比不上前者。

所以,建議使用 ScheduledExecutorService 取代 Timer,當(dāng)然,通過學(xué)習(xí) Timer 會更有助于對 ScheduledExecutorService 的研究。

三方框架 Quartz

除了上述兩種定時任務(wù)框架外,Java 生態(tài)圈還存在一種開源的三方框架,他就是 Quartz。

Quartz 是一個功能完善的任務(wù)調(diào)度框架,支持集群環(huán)境下的任務(wù)調(diào)度,需要將任務(wù)調(diào)度狀態(tài)序列化到數(shù)據(jù)庫。

Quartz 已經(jīng)是隨著分布式概念的流行,成為企業(yè)級定時任務(wù)調(diào)度框架中的不二選擇。

Quartz 這個框架的使用及與原理在本篇就不做介紹了,我們會在后續(xù)介紹分布式概念的時候再來介紹它與 SpringCloud 平臺下的整合使用情況。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • java并發(fā)包JUC同步器框架AQS框架原文翻譯

    java并發(fā)包JUC同步器框架AQS框架原文翻譯

    發(fā)現(xiàn)了一篇JDK作者的論文《The?java.util.concurrent?Synchronizer?Framework》主要描述了作者對AbstractQueuedSynchronizer同步器框架的設(shè)計(jì)和實(shí)現(xiàn)。權(quán)威性毋庸置疑!自然需要拜讀一下,配上中文翻譯,希望大家能有所收獲
    2022-02-02
  • 解析Mybatis SqlSessionFactory初始化原理

    解析Mybatis SqlSessionFactory初始化原理

    本文主要介紹了Mybatis SqlSessionFactory初始化原理,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • Java中Arraylist的最大長度

    Java中Arraylist的最大長度

    這篇文章主要介紹了Java中Arraylist的最大長度,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • SpringBoot+SpringSecurity實(shí)現(xiàn)基于真實(shí)數(shù)據(jù)的授權(quán)認(rèn)證

    SpringBoot+SpringSecurity實(shí)現(xiàn)基于真實(shí)數(shù)據(jù)的授權(quán)認(rèn)證

    Spring Security是一個功能強(qiáng)大且高度可定制的身份驗(yàn)證和訪問控制框架,Spring Security主要做兩個事情,認(rèn)證、授權(quán)。這篇文章主要介紹了SpringBoot+SpringSecurity實(shí)現(xiàn)基于真實(shí)數(shù)據(jù)的授權(quán)認(rèn)證,需要的朋友可以參考下
    2021-05-05
  • Java @Transactional與synchronized使用的問題

    Java @Transactional與synchronized使用的問題

    這篇文章主要介紹了Java @Transactional與synchronized使用的問題,了解內(nèi)部原理是為了幫助我們做擴(kuò)展,同時也是驗(yàn)證了一個人的學(xué)習(xí)能力,如果你想讓自己的職業(yè)道路更上一層樓,這些底層的東西你是必須要會的
    2023-01-01
  • Java程序連接數(shù)據(jù)庫的常用的類和接口介紹

    Java程序連接數(shù)據(jù)庫的常用的類和接口介紹

    這篇文章主要介紹了Java程序連接數(shù)據(jù)庫的常用的類和接口,包括Connection類和Statement類等,需要的朋友可以參考下
    2015-10-10
  • java swing實(shí)現(xiàn)簡單計(jì)算器界面

    java swing實(shí)現(xiàn)簡單計(jì)算器界面

    這篇文章主要為大家詳細(xì)介紹了java swing實(shí)現(xiàn)簡單計(jì)算器界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • springboot實(shí)現(xiàn)攔截器的3種方式及異步執(zhí)行的思考

    springboot實(shí)現(xiàn)攔截器的3種方式及異步執(zhí)行的思考

    實(shí)際項(xiàng)目中,我們經(jīng)常需要輸出請求參數(shù),響應(yīng)結(jié)果,方法耗時,統(tǒng)一的權(quán)限校驗(yàn)等。本文首先為大家介紹 HTTP 請求中三種常見的攔截實(shí)現(xiàn),并且比較一下其中的差異。感興趣的可以了解一下
    2021-07-07
  • 基于JPA中的@Basic注解詳解

    基于JPA中的@Basic注解詳解

    這篇文章主要介紹了JPA中的@Basic注解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 最簡單的在IntelliJ IDEA導(dǎo)入一個本地項(xiàng)目教程(圖文)

    最簡單的在IntelliJ IDEA導(dǎo)入一個本地項(xiàng)目教程(圖文)

    這篇文章主要介紹了最簡單的在IntelliJ IDEA導(dǎo)入一個本地項(xiàng)目教程(圖文),文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08

最新評論