天天看點

【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, l);
        mTimer.schedule(task2, l);
    }

}
           

輸出結果:

任務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(l);
                } 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();
        // 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(l);
                } 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, , TimeUnit.MILLISECONDS);
        newScheduledThreadPool.schedule(task2, , TimeUnit.MILLISECONDS);
    }

}
           

輸出結果如下:

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

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

  1. Timer排程多個TimerTask時,如果其中一個TimerTask抛出異常,其他TimerTask也不會繼續執行了;
  2. Timer在執行周期任務時依賴系統時間,而Sche duledExecutorService執行周期任務是基于延時的,不會随系統時間變化引起執行變化。