【Java多線程】-Timer,TimerTask,ScheduledExecutorService

Timer和TimerTask

TimerTask是一個抽象類,實現了Runnable接口,所以它具備了多線程的能力;
Timer可以看成是一個定時器,用於調度TimerTask執行,一個Timer可以調度任意多個TimerTask,它會將TimerTask存儲在一個隊列中,順序調度。


Timer源碼:

public class Timer {
    /**
     * The timer task queue.  This data structure is shared with the timer
     * thread.  The timer produces tasks, via its various schedule calls,
     * and the timer thread consumes, executing timer tasks as appropriate,
     * and removing them from the queue when they're obsolete.
     */
    private TaskQueue queue = new TaskQueue();

    /**
     * The timer thread.
     */
    private TimerThread thread = new TimerThread(queue); 

Timer是單線程的,內部只有一個線程TimerThread ,有一個任務隊列TaskQueue,用來存儲TimerTask。


使用示例:

package org.iti.thread;

import java.util.Timer;
import java.util.TimerTask;

public class ThreadDemo_5 {
    private static long startAt;

    public static void main(String[] args) {

        Timer mTimer = new Timer();
        TimerTask task1 = new TimerTask() {

            @Override
            public void run() {
                System.out.println(String.format("任務1開始於%s毫秒後",
                        (System.currentTimeMillis() - startAt)));
            }
        };
        TimerTask task2 = new TimerTask() {

            @Override
            public void run() {
                System.out.println(String.format("任務2開始於%s毫秒後",
                        (System.currentTimeMillis() - startAt)));
            }
        };
        startAt = System.currentTimeMillis();
        mTimer.schedule(task1, 1000l);
        mTimer.schedule(task2, 3000l);
    }

}

輸出結果:

任務1開始於1000毫秒後
任務2開始於3005毫秒後

輸出的結果基本符合了我們設定的任務一1000毫秒後執行,任務二3000毫秒後執行,但由於Timer是單線程的,如果任務一執行的時間超過兩個任務之間的時間間隔會出現什麼結果呢?對任務一稍作修改:

TimerTask task1 = new TimerTask() {

            @Override
            public void run() {
                System.out.println(String.format("任務1開始於%s毫秒後",
                        (System.currentTimeMillis() - startAt)));
                try {
                    Thread.sleep(4000l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

輸出結果如下:

任務1開始於1000毫秒後
任務2開始於5040毫秒後

很明顯任務二並沒有按照我們設定在3000毫秒後執行,而是開始於5040毫秒後,爲解決這一問題,我們可以用ScheduledExecutorService來替代Timer,代碼如下:

package org.iti.thread;

import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ThreadDemo_5 {
    private static long startAt;

    public static void main(String[] args) {
        ScheduledExecutorService newScheduledThreadPool = Executors
                .newScheduledThreadPool(2);
        // Timer mTimer = new Timer();
        TimerTask task1 = new TimerTask() {

            @Override
            public void run() {
                System.out.println(String.format("任務1開始於%s毫秒後",
                        (System.currentTimeMillis() - startAt)));
                try {
                    Thread.sleep(4000l);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        TimerTask task2 = new TimerTask() {

            @Override
            public void run() {
                System.out.println(String.format("任務2開始於%s毫秒後",
                        (System.currentTimeMillis() - startAt)));
            }
        };
        startAt = System.currentTimeMillis();
        // mTimer.schedule(task1, 1000l);
        // mTimer.schedule(task2, 3000l);
        newScheduledThreadPool.schedule(task1, 1000, TimeUnit.MILLISECONDS);
        newScheduledThreadPool.schedule(task2, 3000, TimeUnit.MILLISECONDS);
    }

}

輸出結果如下:

任務1開始於1002毫秒後
任務2開始於3003毫秒後

輸出結果基本符合我們的要求。JDK1.5以後,建議用ScheduledExecutorService替代Timer,因爲Timer是單線程的,除了上面的這個缺陷,還有以下兩個缺陷:

  1. Timer調度多個TimerTask時,如果其中一個TimerTask拋出異常,其他TimerTask也不會繼續執行了;
  2. Timer在執行週期任務時依賴系統時間,而Sche duledExecutorService執行週期任務是基於延時的,不會隨系統時間變化引起執行變化。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章