【探索】定時器Timer的使用

定時器Timer的使用

Time類的主要作用就是設置計劃任務,但封裝任務的類卻是TimeTask類,執行計劃任務的代碼要放入TimerTask的子類中,因爲TimeTask是一個抽象類。

1.方法schedule(TimeTask task,Date Time)的測試

該方法的作用是在指定的日期執行一次某一任務。

1.1 執行任務的時間晚於當前時間:在未來執行的效果

創建Run1.java類:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SortedSet;
import java.util.Timer;
import java.util.TimerTask;

public class Run1 {
    public static Timer timer = new Timer();
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("運行了!時間爲:"+new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:10:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
            timer.schedule(task,dateRef);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最後的運行結果爲:

字符串時間:Mon Jun 29 15:10:00 CST 2020當前時間:Mon Jun 29 15:08:48 CST 2020
運行了!時間爲:Mon Jun 29 15:10:00 CST 2020

任務雖然執行完畢,但是能看到進程還未銷燬,呈紅色狀態。

實際上,創建一個Timer就是啓動一個新的線程,這個新啓動的線程並不是守護線程,它一直在運行。

所以解決這個辦法,就是在創建Timer之後將他設值成一個守護線程

public class Run1 {

    public static Timer timer = new Timer(true);
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("運行了!時間爲:"+new Date());
        }
    }

    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:10:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
            timer.schedule(task,dateRef);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

1.2 執行任務的時間早於當前時間:則立即執行

1.3 多個TimerTask及延時的測試

Timer允許有多個TimerTask任務。

創建新的Run2類:

public class Run2 {
    public static Timer timer = new Timer();
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            System.out.println("task1運行了!時間爲:"+new Date());
        }
    }
    static public class MyTask2 extends TimerTask{
        @Override
        public void run() {
            System.out.println("task2運行了!時間爲:"+new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            MyTask2 task2 = new MyTask2();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:31:00";
            String dateString2 = "2020-06-29 15:31:00";
            Date dateRef1 = sdf1.parse(dateString1);
            Date dateRef2 = sdf2.parse(dateString2);
            System.out.println("字符串1時間:"+dateRef1+"當前時間:"+new Date());
            System.out.println("字符串2時間:"+dateRef2+"當前時間:"+new Date());
            timer.schedule(task,dateRef1);
            timer.schedule(task2,dateRef2);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

運行結果爲:

字符串1時間:Mon Jun 29 15:31:00 CST 2020當前時間:Mon Jun 29 15:31:06 CST 2020
字符串2時間:Mon Jun 29 15:31:00 CST 2020當前時間:Mon Jun 29 15:31:06 CST 2020
task1運行了!時間爲:Mon Jun 29 15:31:06 CST 2020
task2運行了!時間爲:Mon Jun 29 15:31:06 CST 2020

TimerTask是以隊列的方式一個一個被順序執行的,所以執行的時間有可能和預期的時間不一致,因爲前面的任務有可能耗時時間較長,則後面的任務運行的時間也會被延遲。

2.方法schedule(TimerTask task, Date firstTime,long period)的測試

該方法的作用是在指定的日期之後,按指定的間隔週期性地無限循環地執行某一任務。

2.1 計劃時間晚於當前時間:在未來執行的效果

修改Run1.java類如下:

public class Run1 {
    public static Timer timer = new Timer();
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("運行了!時間爲:"+new Date());
        }
    }

    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:50:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
            timer.schedule(task,dateRef,4000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

運行結果如下:從運行結果來看,每隔4秒執行一次task任務,並且是無限期的重複執行。

字符串時間:Mon Jun 29 15:50:00 CST 2020當前時間:Mon Jun 29 15:50:17 CST 2020
運行了!時間爲:Mon Jun 29 15:50:17 CST 2020
運行了!時間爲:Mon Jun 29 15:50:21 CST 2020
運行了!時間爲:Mon Jun 29 15:50:25 CST 2020

2.2 計劃時間早於當前時間:立即執行

2.3 任務執行時間同樣會有被延時的可能

3. TimerTask的Cancel方法

TimerTask類中的cancel()方法的作用是將自身從任務隊列中清除。

新建Run3.java:

public class Run3 {
    public static Timer timer = new Timer();
    static public class MyTaskA extends TimerTask{
        @Override
        public void run() {
            System.out.println("A運行了!時間爲:"+new Date());
            this.cancel();//取消自身
        }
    }
    static public class MyTaskB extends TimerTask{
        @Override
        public void run() {
            System.out.println("B運行了!時間爲:"+new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTaskA task = new MyTaskA();
            MyTaskB task2 = new MyTaskB();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:56:00";
            Date dateRef2 = sdf1.parse(dateString1);
            System.out.println("字符串2時間:"+dateRef2+"當前時間:"+new Date());
            timer.schedule(task,dateRef2,4000);
            timer.schedule(task2,dateRef2,4000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最後的運行結果如下:A在之後的循環執行中不再執行

字符串2時間:Mon Jun 29 15:56:00 CST 2020當前時間:Mon Jun 29 15:56:51 CST 2020
A運行了!時間爲:Mon Jun 29 15:56:51 CST 2020
B運行了!時間爲:Mon Jun 29 15:56:51 CST 2020
B運行了!時間爲:Mon Jun 29 15:56:55 CST 2020

4.Timer類的cancel()方法

Timer類的cancel()和TimerTask的cancel()不同,Timer類中的cancel()的作用是將任務隊列中的全部任務清空。

修改Run3.java如下:

public class Run3 {
    public static Timer timer = new Timer();
    static public class MyTaskA extends TimerTask{
        @Override
        public void run() {
            System.out.println("A運行了!時間爲:"+new Date());
            timer.cancel();//取消自身
        }
    }
    static public class MyTaskB extends TimerTask{
        @Override
        public void run() {
            System.out.println("B運行了!時間爲:"+new Date());
        }
    }
    public static void main(String[] args) {
        try {
            MyTaskA task = new MyTaskA();
            MyTaskB task2 = new MyTaskB();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:56:00";
            Date dateRef2 = sdf1.parse(dateString1);
            System.out.println("字符串2時間:"+dateRef2+"當前時間:"+new Date());
            timer.schedule(task,dateRef2,4000);
            timer.schedule(task2,dateRef2,4000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最後的運行結果如下,全部任務被清除,且進程被銷燬,按鈕變爲灰色

字符串2時間:Mon Jun 29 15:56:00 CST 2020當前時間:Mon Jun 29 15:59:33 CST 2020
A運行了!時間爲:Mon Jun 29 15:59:33 CST 2020

Timer類中的cancel()使用注意事項:

  • Timer類中的cancel()方法有時並不一定會停止執行計劃任務,而是正常執行,這是因爲Timer類中cancel()方法有時並沒有爭搶到Queue鎖,所以TimerTask類中的任務可以正常被執行。

5.方法schedule(TimerTask task,long delay)方法的測試

該方法的作用是以執行schedule(TimerTask task,long delay)方法當前的時間爲參考時間,在此時間基礎上延遲指定的毫秒數執行一次TimerTask任務。

6.方法schedule(TimerTask task,long delay,long period)方法的測試

該方法的作用是以執行schedule(TimerTask task,long delay)方法當前的時間爲參考時間,在此時間基礎上延遲指定的毫秒數執行一次TimerTask任務,再以某一間隔時間無限次數地執行某一任務。

7.方法scheduleAtFixedRate(TimerTask task,Date firstTime,long period)方法的測試

  • 方法schedule和方法scheduleAtFixedRate都會按順序執行,所以不用考慮非線程安全的情況

  • 方法schedule和方法scheduleAtFixedRate主要的區別只在於是否具有追趕性的情況

  • 使用schedule方法:如果執行任務的時間沒有被延時,那麼下一次任務的執行時間參考的是上一次任務的“開始”時間來計算。

  • 使用scheduleAtFixedRate方法:如果執行任務的時間沒有被延時,那麼下一次任務的執行時間參考的是上一次任務的“開始”時間來計算。

  • 延時的情況則沒有區別,兩者都是下一次執行的時間參考的都是上一次任務“結束”時的時間來計算。

7.1 測試schedule不延時的情況

創建Run4.java類:

public class Run4 {
    private static Timer timer = new Timer();
    private static int runCount = 0;
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            try {
                System.out.println("1運行了!時間爲:"+new Date());
                Thread.sleep(1000);
                System.out.println(" 1 end 了!時間爲:"+new Date());
                runCount++;
                if(runCount == 5){
                    timer.cancel();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:50:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
            timer.schedule(task,dateRef,3000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最後的運行結果如下:在不延時的情況下,如果執行任務的時間沒有被延時,則下一次執行任務的時間是上一次任務的開始時間加上delay的時間

字符串時間:Mon Jun 29 15:50:00 CST 2020當前時間:Mon Jun 29 16:14:23 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:23 CST 2020
 1 end 了!時間爲:Mon Jun 29 16:14:24 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:26 CST 2020
 1 end 了!時間爲:Mon Jun 29 16:14:27 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:29 CST 2020
 1 end 了!時間爲:Mon Jun 29 16:14:30 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:32 CST 2020
 1 end 了!時間爲:Mon Jun 29 16:14:33 CST 2020
1運行了!時間爲:Mon Jun 29 16:14:35 CST 2020
 1 end 了!時間爲:Mon Jun 29 16:14:36 CST 2020

7.2 測試schedule延時的情況

public class Run2 {
    private static Timer timer = new Timer();
    private static int runCount = 0;
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            try {
                System.out.println("1運行了!時間爲:"+new Date());
                Thread.sleep(5000);
                System.out.println("1結束了!時間爲:"+new Date());
                runCount++;
                if(runCount == 2){
                    timer.cancel();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:31:00";
            Date dateRef1 = sdf1.parse(dateString1);
            System.out.println("字符串1時間:"+dateRef1+"當前時間:"+new Date());
            timer.schedule(task,dateRef1,2000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最後的運行結果如下:如果任務被延時,那麼下一次任務的執行時間以上一次任務“結束”時的時間來參考計算。

字符串1時間:Mon Jun 29 15:31:00 CST 2020當前時間:Mon Jun 29 16:26:28 CST 2020
1運行了!時間爲:Mon Jun 29 16:26:28 CST 2020
1結束了!時間爲:Mon Jun 29 16:26:33 CST 2020
1運行了!時間爲:Mon Jun 29 16:26:33 CST 2020
1結束了!時間爲:Mon Jun 29 16:26:38 CST 2020

7.3 測試scheduleAtFixedRate方法任務不延時的情況

修改Run4.java類如下:

public class Run4 {
    private static Timer timer = new Timer();
    private static int runCount = 0;
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            try {
                System.out.println("1運行了!時間爲:"+new Date());
                Thread.sleep(1000);
                System.out.println(" 1 end 了!時間爲:"+new Date());
                runCount++;
                if(runCount == 2){
                    timer.cancel();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2020-06-29 15:50:00";
            Date dateRef = sdf.parse(dateString);
            System.out.println("字符串時間:"+dateRef+"當前時間:"+new Date());
            timer.scheduleAtFixedRate(task,dateRef,3000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最後的運行結果爲下:在不延時的情況下,如果執行任務的時間沒有被延時,則下一次執行任務的時間是上一次任務的開始時間加上delay的時間

字符串時間:Mon Jun 29 15:50:00 CST 2020當前時間:Mon Jun 29 16:28:39 CST 2020
1運行了!時間爲:Mon Jun 29 16:28:39 CST 2020
 1 end 了!時間爲:Mon Jun 29 16:28:40 CST 2020
1運行了!時間爲:Mon Jun 29 16:28:40 CST 2020
 1 end 了!時間爲:Mon Jun 29 16:28:41 CST 2020

7.4 測試scheduleAtFixedRate方法任務延時的情況

修改Run2.java類如下:

public class Run2 {
    private static Timer timer = new Timer();
    private static int runCount = 0;
    static public class MyTask1 extends TimerTask{
        @Override
        public void run() {
            try {
                System.out.println("1運行了!時間爲:"+new Date());
                Thread.sleep(5000);
                System.out.println("1結束了!時間爲:"+new Date());
                runCount++;
                if(runCount == 2){
                    timer.cancel();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        try {
            MyTask1 task = new MyTask1();
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString1 = "2020-06-29 15:31:00";
            Date dateRef1 = sdf1.parse(dateString1);
            System.out.println("字符串1時間:"+dateRef1+"當前時間:"+new Date());
            timer.scheduleAtFixedRate(task,dateRef1,2000);
        }catch (ParseException e){
            e.printStackTrace();
        }
    }
}

最後的運行結果如下:如果任務被延時,那麼下一次任務的執行時間以上一次任務“開始”時的時間來參考計算。

字符串1時間:Mon Jun 29 15:31:00 CST 2020當前時間:Mon Jun 29 16:31:00 CST 2020
1運行了!時間爲:Mon Jun 29 16:31:00 CST 2020
1結束了!時間爲:Mon Jun 29 16:31:05 CST 2020
1運行了!時間爲:Mon Jun 29 16:31:05 CST 2020
1結束了!時間爲:Mon Jun 29 16:31:10 CST 2020

8.驗證schedule不具有追趕性

創建Run6.java類如下:

public class Run6 {
    public static Timer timer = new Timer();
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("運行了!時間爲:"+new Date());
            System.out.println("結束運行了!時間爲:"+new Date());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyTask myTask = new MyTask();
        System.out.println("現在執行時間爲:" + new Date());
        Calendar c = Calendar.getInstance();
        c.set(Calendar.SECOND, c.get(Calendar.SECOND) +20);
        Date runDate = c.getTime();
        System.out.println("計劃執行時間爲:" + runDate);
        Timer timer = new Timer();
        //調用的是schedule方法,驗證其不具追趕性
        timer.schedule(myTask, runDate, 4000);

    }
}

運行結果如下:Mon Jun 29 16:43:36 CST 2020到Mon Jun 29 16:43:56 CST 2020之間的時間被取消掉,不執行了,這就是Task任務不追趕。

現在執行時間爲:Mon Jun 29 16:43:36 CST 2020
計劃執行時間爲:Mon Jun 29 16:43:56 CST 2020
運行了!時間爲:Mon Jun 29 16:43:56 CST 2020
結束運行了!時間爲:Mon Jun 29 16:43:56 CST 2020
運行了!時間爲:Mon Jun 29 16:44:00 CST 2020
結束運行了!時間爲:Mon Jun 29 16:44:00 CST 2020

9.驗證scheduleAtFixedRate具有追趕性

修改Run6.java如下:

public class Run6 {
    public static Timer timer = new Timer();
    static public class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("運行了!時間爲:"+new Date());
            System.out.println("結束運行了!時間爲:"+new Date());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyTask myTask = new MyTask();
        System.out.println("現在執行時間爲:" + new Date());
        Calendar c = Calendar.getInstance();
        c.set(Calendar.SECOND, c.get(Calendar.SECOND) -20);
        Date runDate = c.getTime();
        System.out.println("計劃執行時間爲:" + runDate);
        Timer timer = new Timer();
        //調用的是schedule方法,驗證其不具追趕性
        timer.scheduleAtFixedRate(myTask, runDate, 4000);
    }
}

最後運行結果如下:16:51:38-16:51:58的時間被補充執行:本來我的時間間隔爲4s執行一次,但是前期沒有按照我的時間間隔來執行。

追趕性就是如果我們設置任務開始的時間在當前時間之前,那麼他會計算出兩個時間的差,計算出時間差內能夠執行任務多少次,然後立即執行,執行完這個次數之後再按照我定的時間間隔做

現在執行時間爲:Mon Jun 29 17:05:07 CST 2020
計劃執行時間爲:Mon Jun 29 17:04:47 CST 2020
運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
結束運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
結束運行了!時間爲:Mon Jun 29 17:05:07 CST 2020
運行了!時間爲:Mon Jun 29 17:05:07 CST 2020

當執行任務的時間大於週期間隔時,會發生什麼呢?
(1)schedule方法:下一次執行時間相對於 上一次 實際執行完成的時間點 ,因此執行時間會不斷延後
(2)scheduleAtFixedRate方法:下一次執行時間相對於上一次開始的 時間點 ,因此執行時間不會延後,存在併發性

使用schedule:下一次的執行時間點=上一次程序執行完成的時間點+間隔時間

使用scheduleAtFixedRate:下一次的執行時間點=上一次程序開始執行的時間點+間隔時間 ;並且如果間隔時間爲5秒,因爲前一個任務要執行>5秒,而當前任務已經開始執行了,因此兩個任務間存在重疊,需要考慮線程同步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章