本文來自我一個朋友的我的博客(但願各位之後多多支持):https://www.liupeng.mobi/archives/777java
在java中一個完整的定時任務須要由Timer和TimerTask兩個類配合完成。json
而TimerTask是由Timer安排執行一次或者重複執行的任務。多線程
Timer中提供了四個構造方法:併發
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工具
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 date = new Date(this.scheduledExecutionTime());
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類是一種簡單實用的實現定時任務的方法,然而它存在着自身的缺陷:
(1)Timer對調度的支持是基於絕對時間而不是相對時間,所以它對於系統時間的改變很是敏感;
(2)Timer線程是不會捕獲異常的,若是TimerTask拋出未檢查的異常則會致使Timer線程終止,同時Timer也不會從新恢復線程的執行,它會錯誤的認爲整個Timer線程都會取消,已經被安排但還沒有執行的TimerTask也不會再執行了,新的任務也不能被調度。所以,若是TimerTask拋出未檢查的異常,Timer將會產生沒法預料的行爲。
ScheduledExecutorService是基於相對時間;Timer內部是單一線程,
而ScheduledThreadPoolExecutor內部是個線程池,能夠支持多個任務併發執行。
ScheduledExecutor的設計思想是每個被調度的任務都會由線程池中一個線程去執行,所以任務是併發的,相互之間不會受到干擾;只有當任務的時間到來時,ScheduledExecutor纔會真正啓動一個線程。
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在執行定時任務時只會建立一個工做線程,當工做線程由於某種緣由而致使線程任務執行時間過長,超過了兩個任務的間隔時間,則會出現以上狀況。
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毫秒
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)
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毫秒
能夠看到針對Timer類存在的兩個缺陷,ScheduledExecutorService能夠很好地解決,其思路主要在於每個被調度的任務都是由線程池中的一個線程去執行,任務之間是併發的,不會互相干擾。