Java 定時任務---Timer

本文來自我一個朋友的我的博客(但願各位之後多多支持):https://www.liupeng.mobi/archives/777java

1、Timer類

java中一個完整的定時任務須要由Timer和TimerTask兩個類配合完成。json

  • 其中Timer是一種工具,線程用其安排在後臺線程中執行的任務,可安排任務執行一次或者按期重複執行;
  • 而TimerTask是由Timer安排執行一次或者重複執行的任務。多線程

Timer中提供了四個構造方法:併發

  • schedule(TimerTask task, Date time) ——安排在指定的時間執行指定的任務;
  • schedule(TimerTask task, Date firstTime, long period) ——安排指定的任務在指定的時間開始進行重複的固定延遲執行;
  • schedule(TimerTask task, long delay) ——安排在指定延遲後執行指定的任務;
  • schedule(TimerTask task, long delay, long period) ——安排指定的任務在指定的延遲後開始進行重複的固定速率執行。

一、在指定延遲時間執行定時任務

 1 public class TestUserTimer {  2     public static void main(String[] args) {  3         System.out.println("任務開始" +  new Date());  4  timer();  5  }  6 
 7 // 方法一;設定指定任務task在指定時間time執行schedule(TimeerTask,Date time)
 8     private static void timer(){  9         Timer timer = new Timer(); 10         timer.schedule(new TimerTask() { 11  @Override 12             public void run() { 13                 System.out.println("2秒後執行一次該任務" + new Date()); 14  } 15         },2000);   //單位毫秒ms,延遲2秒後執行
16  } 17 }

執行結果:ide

任務開始Thu May 31 14:37:33 CST 2018
2秒後執行一次該任務Thu May 31 14:37:35 CST 2018工具

2. 在指定時間執行定時任務

 1 public class TestUserTimer {  2     public static void main(String[] args) {  3         System.out.println("指定的時間爲 14:47");  4  timer();  5  }  6 
 7 // 方法二;在指定時間執行任務
 8     private static void timer(){  9         Calendar calendar = Calendar.getInstance(); 10         calendar.set(Calendar.HOUR_OF_DAY,14); 11         calendar.set(Calendar.MINUTE,47); 12         calendar.set(Calendar.SECOND,0); 13         final Date time  = calendar.getTime(); 14         Timer timer = new Timer(); 15         timer.schedule(new TimerTask() { 16  @Override 17             public void run() { 18                 Date date = new Date(this.scheduledExecutionTime()); 19                 System.out.println("14:47時執行一次該任務: " + date); 20  } 21  },time); 22  } 23 }

運行結果爲:oop

指定的時間爲 14:47
14:47時執行一次該任務: Thu May 31 14:47:00 CST 2018優化

  • Date 日期類 在JDK1.0中,Date類是惟一的一個表明時間的類,可是因爲Date類不便於實現國際化,因此從JDK1.1版本開始,推薦使用Calendar類進行時間和日期處理。這裏簡單介紹一下Date類的使用。
    • 輸出格式: Thu May 31 14:47:00 CST 2018
    • 使用Date類表明指定的時間
    • Date date = new Date(this.scheduledExecutionTime());
  • Calendar 日曆類注意: 若是延遲執行時間在當前時間以前,則當即執行
    • 從JDK1.1版本開始,在處理日期和時間時,系統推薦使用Calendar類進行實現。
    • Calender.HOUR 12小時制
    • Calender.HOUR_OF_DAY 24小時制
  • 注意: 若是延遲執行時間在當前時間以前,則當即執行

3. 在延遲指定時間後以指定的間隔時間循環執行定時任務

 1 public class TestUserTimer {  2     public static void main(String[] args) {  3         System.out.println("任務開始");  4  timer();  5  }  6 
 7 // 在延遲指定時間後以指定的間隔時間循環執行定時任務
 8     private static void timer(){  9         Timer timer = new Timer(); 10         timer.schedule(new TimerTask() { 11  @Override 12             public void run() { 13                 System.out.println("執行一次該任務: " + new Date()); 14  } 15         },2000,1000); 16  } 17 }

 

輸出結果:this

任務開始
執行一次該任務: Thu May 31 15:35:49 CST 2018
執行一次該任務: Thu May 31 15:35:50 CST 2018
執行一次該任務: Thu May 31 15:35:51 CST 2018
執行一次該任務: Thu May 31 15:35:52 CST 2018
執行一次該任務: Thu May 31 15:35:53 CST 2018spa

四、Timer類小結

Timer類是一種簡單實用的實現定時任務的方法,然而它存在着自身的缺陷:

(1)Timer對調度的支持是基於絕對時間而不是相對時間,所以它對於系統時間的改變很是敏感;

(2)Timer線程是不會捕獲異常的,若是TimerTask拋出未檢查的異常則會致使Timer線程終止,同時Timer也不會從新恢復線程的執行,它會錯誤的認爲整個Timer線程都會取消,已經被安排但還沒有執行的TimerTask也不會再執行了,新的任務也不能被調度。所以,若是TimerTask拋出未檢查的異常,Timer將會產生沒法預料的行爲。

2、ScheduledExecutorService

ScheduledExecutorService是基於相對時間;Timer內部是單一線程,

而ScheduledThreadPoolExecutor內部是個線程池,能夠支持多個任務併發執行。

ScheduledExecutor的設計思想是每個被調度的任務都會由線程池中一個線程去執行,所以任務是併發的,相互之間不會受到干擾;只有當任務的時間到來時,ScheduledExecutor纔會真正啓動一個線程。

一、Timer的第一個缺陷

 1 public class TestUserTimer {  2     private Timer timer;  3     private long start;  4 
 5     public TestUserTimer() {  6         this.timer = new Timer();  7         start = System.currentTimeMillis();  8  }  9 
10     /**
11  * 延遲一秒執行調度任務,輸出後睡眠4 秒 12      */
13     public void timerOne(){ 14         timer.schedule(new TimerTask() { 15  @Override 16             public void run() { 17                 System.out.println("任務1開始執行: " + (System.currentTimeMillis()- start) +"毫秒"); 18                 try { 19                     Thread.sleep(4000); 20                 } catch (InterruptedException e) { 21  e.printStackTrace(); 22  } 23  } 24         },1000); 25  } 26 
27     public void timerTwo(){ 28         timer.schedule(new TimerTask() { 29  @Override 30             public void run() { 31                 System.out.println("任務2開始執行: " + (System.currentTimeMillis()-start) + "毫秒"); 32  } 33         },3000); 34  } 35     public static void main(String[] args) { 36         TestUserTimer testUserTimer = new TestUserTimer(); 37         System.out.println("任務開始: " + testUserTimer.start); 38  testUserTimer.timerOne(); 39  testUserTimer.timerTwo(); 40  } 41 }

運行結果:

任務開始: 1527753568049
任務1開始執行: 1007毫秒
任務2開始執行: 5007毫秒

按照設想,任務1與開始時間間隔爲1秒,而任務2與開始時間的時間間隔爲3秒。然而,因爲Timer在執行定時任務時只會建立一個工做線程,當工做線程由於某種緣由而致使線程任務執行時間過長,超過了兩個任務的間隔時間,則會出現以上狀況。

  • 上述代碼說明Timer類是單線程的.

使用ScheduledExecutorService優化:

 1 public class TestScheduledExecutorTimer {  2 
 3     private ScheduledExecutorService schedule;  4     private long start;  5 
 6     public TestScheduledExecutorTimer() {  7         this.schedule =  Executors.newScheduledThreadPool(2); //執行器建立預期的線程池
 8         start = System.currentTimeMillis();  9  } 10 
11 
12     public void timerOne(){ 13         schedule.schedule(new Runnable() { 14  @Override 15             public void run() { 16                 System.out.println("任務開始執行,與開始時間的時間間隔爲: " +(System.currentTimeMillis() - start) + "毫秒"); 17                 try { 18                     Thread.sleep(4000); 19                 } catch (InterruptedException e) { 20  e.printStackTrace(); 21  } 22  } 23         },1000, TimeUnit.MILLISECONDS); 24  } 25 
26     public void timerTwo(){ 27         schedule.schedule(new Runnable() { 28  @Override 29             public void run() { 30                 System.out.println("任務2開始執行: " + (System.currentTimeMillis()-start) + "毫秒"); 31  } 32         },2000,TimeUnit.MILLISECONDS); 33  } 34 
35     public static void main(String[] args) { 36         TestScheduledExecutorTimer testScheduledExecutorTimer = new TestScheduledExecutorTimer(); 37         System.out.println("任務開始: " + testScheduledExecutorTimer.start); 38  testScheduledExecutorTimer.timerOne(); 39  testScheduledExecutorTimer.timerTwo(); 40  } 41 }

 

輸出結果:

任務開始: 1527760168681
任務開始執行,與開始時間的時間間隔爲: 1004毫秒
任務2開始執行: 2005毫秒

  • 上述代碼說明ScheduledExecutorService是多線程並行的

2.Timer的第二個缺陷

 1 public class TestTimerDemo {  2 
 3     private Timer timer;  4     private long start;  5 
 6     public TestTimerDemo() {  7         this.timer = new Timer();  8         start = System.currentTimeMillis();  9  } 10 
11 
12     public void timerOne(){ 13          timer.schedule(new TimerTask() { 14  @Override 15             public void run() { 16                throw  new RuntimeException(); 17  } 18         },1000); 19  } 20 
21     public void timerTwo(){ 22         timer.schedule(new TimerTask() { 23  @Override 24             public void run() { 25                 System.out.println("任務2開始執行: " + new Date()); 26  } 27         },3000); 28  } 29 
30     public static void main(String[] args) { 31         TestTimerDemo timerDemo = new TestTimerDemo(); 32         System.out.println("任務開始: " + timerDemo.start); 33  timerDemo.timerOne(); 34  timerDemo.timerTwo(); 35  } 36 }

輸出結果:

任務開始: 1527761009205
Exception in thread 「Timer-0」 java.lang.RuntimeException
at com.sojson.test.TestTimerDemo$1.run(TestTimerDemo.java:45)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)

  • 上述代碼表名
    • timerOne拋出異常,而timerTwo並無執行。

使用ScheduledExecutorService優化:

 1 public class TestScheduledExecutorTimer {  2 
 3     private ScheduledExecutorService schedule;  4     private long start;  5 
 6     public TestScheduledExecutorTimer() {  7         this.schedule =  Executors.newScheduledThreadPool(2); //執行器建立預期的線程池
 8         start = System.currentTimeMillis();  9  } 10 
11     /**
12  * 延遲一秒執行調度任務,輸出後睡眠4 秒 13      */
14     public void timerOne(){ 15         schedule.schedule(new Runnable() { 16  @Override 17             public void run() { 18                 throw new RuntimeException(); 19  } 20         },1000, TimeUnit.MILLISECONDS); 21  } 22 
23     public void timerTwo(){ 24         schedule.schedule(new Runnable() { 25  @Override 26             public void run() { 27                 System.out.println("任務2開始執行: " + (System.currentTimeMillis()-start) + "毫秒"); 28  } 29         },2000,TimeUnit.MILLISECONDS); 30  } 31 
32     public static void main(String[] args) { 33         TestScheduledExecutorTimer testScheduledExecutorTimer = new TestScheduledExecutorTimer(); 34         System.out.println("任務開始: " + testScheduledExecutorTimer.start); 35  testScheduledExecutorTimer.timerOne(); 36  testScheduledExecutorTimer.timerTwo(); 37  } 38 }

 

輸出結果:

任務開始: 1527761726364
任務2開始執行: 2025毫秒

  • 上述代碼說明:ScheduledExcetorService是多線程的,線程之間互不影響.

3.ScheduledExecutorService小結

能夠看到針對Timer類存在的兩個缺陷,ScheduledExecutorService能夠很好地解決,其思路主要在於每個被調度的任務都是由線程池中的一個線程去執行,任務之間是併發的,不會互相干擾。

相關文章
相關標籤/搜索