Java定時/延時任務(wù)之Timer用法詳解
1. 概要
上一篇文章地址:定時/延時任務(wù)-自己實現(xiàn)一個簡單的定時器
在上一篇文章中,我們自己實現(xiàn)了一個簡單的 Timer 并提出了一些缺點,下面我們就來看看 JDK 中的 Timer 用法
2. 簡述
在 Java Development Kit (JDK) 中,java.util.Timer 是一個用于調(diào)度任務(wù)的工具類。Timer 類使用一個后臺線程來遍歷隊列中的任務(wù),同時可以按照固定的延時或者固定的速率來重復(fù)執(zhí)行。
首先在介紹 Timer 的 API 之前,先來看兩個概念:
2.1 固定速率
固定速率 策略表示任務(wù)在固定的時間間隔內(nèi)重復(fù)執(zhí)行,不管任務(wù)的執(zhí)行時間有多長,如果任務(wù)的執(zhí)行時間超過了時間間隔,那么下一個任務(wù)會在當前任務(wù)執(zhí)行完畢之后就會馬上開始執(zhí)行,下面是一個例子:假設(shè)我們設(shè)置了一個固定速率為 5 的任務(wù),從 0s 開始執(zhí)行,也就是說這個任務(wù) 5s 執(zhí)行一次:
- 第一次執(zhí)行: 在 0s 開始執(zhí)行一次,假設(shè)執(zhí)行時間是 3s
- 第二次執(zhí)行: 在 5s 開始執(zhí)行第二次,假設(shè)執(zhí)行的時候被阻塞了,執(zhí)行了 8s
- 第三次執(zhí)行: 在 13s 開始執(zhí)行第三次,假設(shè)執(zhí)行的時候被阻塞了,執(zhí)行了 3s
- 第四次執(zhí)行: 在 16s 開始執(zhí)行第四次,假設(shè)執(zhí)行的時候沒有被阻塞
- 第四次執(zhí)行: 在 20s 開始執(zhí)行第五次,假設(shè)執(zhí)行的時候沒有被阻塞
- …
看了上面的過程分析,你可能有點懵,沒關(guān)系,等到下面的時候會有例子并解釋
2.2 固定延時
固定延時 策略表示任務(wù)在當前任務(wù)執(zhí)行完成之后,固定延時一段時間再執(zhí)行下一個任務(wù),下面是一個例子:假設(shè)我們設(shè)置了一個固定速率為 5 的任務(wù),從 0s 開始執(zhí)行,也就是說這個任務(wù) 5s 執(zhí)行一次:
- 第一次執(zhí)行: 在 0s 開始執(zhí)行一次,假設(shè)執(zhí)行時間是 3s
- 第二次執(zhí)行: 在 5s 開始執(zhí)行第二次,假設(shè)執(zhí)行的時候被阻塞了,執(zhí)行了 8s
- 第三次執(zhí)行: 在 13s 開始執(zhí)行第三次,假設(shè)執(zhí)行的時候被阻塞了,執(zhí)行了 3s
- 第四次執(zhí)行: 在 18s 開始執(zhí)行第四次,假設(shè)執(zhí)行的時候沒有被阻塞
- 第四次執(zhí)行: 在 23s 開始執(zhí)行第五次,假設(shè)執(zhí)行的時候沒有被阻塞
- …
2.3 區(qū)別
看了上面兩種方式的分析,可以做一個小的總結(jié):
固定速率: 任務(wù)會按照固定時間間隔執(zhí)行,如果任務(wù)執(zhí)行的時間大于時間間隔,那么下一個任務(wù)會馬上執(zhí)行
固定延時: 任務(wù)會按照固定延時執(zhí)行,如果任務(wù)執(zhí)行的時間小于時間間隔,那么兩次任務(wù)的執(zhí)行時間間隔就是設(shè)置的延時;如果任務(wù)執(zhí)行的時間大于時間間隔,那么兩次任務(wù)執(zhí)行的時間間隔就是任務(wù)執(zhí)行的時間,也就是說下一次任務(wù)會馬上執(zhí)行
固定速率 這種方式比適合用于嚴格要求按照時間間隔執(zhí)行的任務(wù),比如心跳探測、數(shù)據(jù)收集等…
固定延時 執(zhí)行任務(wù)的時間不固定,但是得確保每一次任務(wù)執(zhí)行完之后有一定時間間隔再執(zhí)行下一次的任務(wù),比如日志收集、數(shù)據(jù)清理等…
3. Timer 的用法
下面我們就來介紹下 Timer 的幾個 API 的用法
3.1 固定延時 - public void schedule(TimerTask task, long delay, long period)
這個 API 的意思是:延時 delay 后開始按照 period 的間隔執(zhí)行
public class Pra { public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if(Math.random() < 0.5){ System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } } }, 0, 5000); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
輸出結(jié)果如下:
3.1.1 解釋
解釋:Timer 源碼中對于任務(wù)的添加是線程被喚醒后獲取到任務(wù),之后就會立馬計算出下一次該任務(wù)的調(diào)度時間加入隊列中
有了上面的基礎(chǔ)再來看輸出,這就是為什么任務(wù)執(zhí)行了 3s 最終還是在 49s 就開始執(zhí)行下一個任務(wù),因為 44s 執(zhí)行任務(wù)的時候會根據(jù)當前執(zhí)行時間算出下一個任務(wù)執(zhí)行時間應(yīng)該是 44 + 5 = 49s,而第二個任務(wù) 49s 執(zhí)行的時候會算出下一個任務(wù)執(zhí)行時間是 49 + 5 = 54s,但是由于任務(wù)執(zhí)行時間長達 8s,導(dǎo)致下一個任務(wù)根本沒時間被調(diào)度,所以只能在 57s 執(zhí)行完之后去看隊列,發(fā)現(xiàn)隊列里面 54s 的任務(wù)早就到時間了,這時候算出下一個任務(wù)的執(zhí)行時間是 57 + 5 = 02s,于是把這個任務(wù)加入到隊列中,然后立馬調(diào)度這個 54s 的任務(wù),以此類推
你可能會有疑問:如果我任務(wù)執(zhí)行時間是 8s,任務(wù)間隔是 3s,不會導(dǎo)致執(zhí)行完一個任務(wù)之后隊列中會有多個沒有執(zhí)行的任務(wù)嗎?并不會,因為固定延時是按照當前時間來算下一個任務(wù)的計算時間,所以任務(wù)執(zhí)行時間大于任務(wù)間隔時間的前提下,不管你間隔多少,都是以任務(wù)執(zhí)行時間為主
但是對于固定速率又不一樣了,這個我們下面會說
3.2 固定延時 - public void schedule(TimerTask task, Date firstTime, long period)
顧名思義,就是設(shè)置一個第一次啟動的時間點,然后以 period 的延時執(zhí)行,看下面的例子:
public class Pra { public static void main(String[] args) { Timer timer = new Timer(); System.out.println("start time = " + getTime()); timer.schedule(new TimerTask() { @Override public void run() { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if(Math.random() < 0.5){ System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } } }, new Date(System.currentTimeMillis() + 10000), 5000); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
上面 3.1 已經(jīng)有解釋了
3.3 固定速率 - public void scheduleAtFixedRate(TimerTask task, long delay, long period)
當前延時 delay 開始,接著每次執(zhí)行的間隔是 period
public class Pra { public static void main(String[] args) { Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if(Math.random() < 0.5){ System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } } }, 0, 5000); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
3.3.1 解釋
1.首先在 27s 開始執(zhí)行第一次,然后往隊列立馬加入一個 32s 的下一次執(zhí)行的任務(wù),當前任務(wù)執(zhí)行時間 3s
2. 第二次在 32s 開始執(zhí)行第而次,然后往隊列立馬加入一個 37s 的下一次執(zhí)行的任務(wù),當前任務(wù)執(zhí)行時間 8s
3. 第三次執(zhí)行,當 工作線程 執(zhí)行上一個任務(wù)之后已經(jīng)到 40s 了,由于 40s 已經(jīng)超過了 37s 的延時任務(wù)執(zhí)行時間,于是會立馬開始執(zhí)行,這時候往隊列里面添加一個 42s 執(zhí)行的任務(wù)(下一次執(zhí)行)
4. 第四次執(zhí)行,當前任務(wù)執(zhí)行時間 3s,執(zhí)行完已經(jīng) 43s 了,這時候執(zhí)行結(jié)束會發(fā)現(xiàn)隊列里面的 42s 的任務(wù)已經(jīng)過期了,就會往隊列立馬添加一個 47s 的任務(wù)(下一次執(zhí)行),然后立馬開始執(zhí)行,所以第四次執(zhí)行時間是 43s
5. 第四次執(zhí)行的時間是 3s ,執(zhí)行完任務(wù)是 46s,這時候沒到 47s,所以沒有到下一次任務(wù)的執(zhí)行時間,繼續(xù)等待到 47s 執(zhí)行 第五次任務(wù)
3.4 固定速率 - public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
和上面一樣就是選擇某個首次執(zhí)行時間點開始執(zhí)行,后續(xù)速率 period
public class Pra { public static void main(String[] args) { Timer timer = new Timer(); System.out.println("start time = " + getTime()); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if(Math.random() < 0.5){ System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } } }, new Date(), 5000); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
3.5 非周期任務(wù) - public void schedule(TimerTask task, Date time)
上面都是周期任務(wù),下面這兩個就是非周期任務(wù),顧名思義就是只執(zhí)行一次的任務(wù),來看例子:
public class Pra { public static void main(String[] args) { Timer timer = new Timer(); System.out.println("start time = " + getTime()); timer.schedule(new TimerTask() { @Override public void run() { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if(Math.random() < 0.5){ System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } } }, new Date(System.currentTimeMillis() + 10000)); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
3.6 非周期任務(wù) - schedule(TimerTask task, long delay)
延遲 delay 時間之后開始執(zhí)行
public class Pra { public static void main(String[] args) { Timer timer = new Timer(); System.out.println("start time = " + getTime()); timer.schedule(new TimerTask() { @Override public void run() { try { System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime()); if(Math.random() < 0.5){ System.out.println("sleep: 3s"); Thread.sleep(3000); } else { System.out.println("sleep: 8s"); Thread.sleep(8000); } } catch (InterruptedException e) { e.printStackTrace(); } } }, 10000); } private static String getTime() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"); return sdf.format(new Date()); } }
以上就是Java定時/延時任務(wù)之Timer用法詳解的詳細內(nèi)容,更多關(guān)于Java Timer用法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java實現(xiàn)mongodb的數(shù)據(jù)庫連接池
這篇文章主要介紹了基于java實現(xiàn)mongodb的數(shù)據(jù)庫連接池,Java通過使用mongo-2.7.3.jar包實現(xiàn)mongodb連接池,感興趣的小伙伴們可以參考一下2015-12-12- String在棧中,StringBuffer在堆中!所以String是不可變的,數(shù)據(jù)是共享的。StringBuffer都是獨占的,是可變的(因為每次都是創(chuàng)建新的對象?。?/div> 2015-11-11
Java 時間格式轉(zhuǎn)換之impleDateFormat與Data API解析與使用
想必大家對 SimpleDateFormat 并不陌生。SimpleDateFormat 是 Java 中一個非常常用的類,他是以區(qū)域敏感的方式格式化和解析日期的具體類。 它允許格式化 (date -> text)、語法分析 (text -> date)和標準化2021-11-11最新評論