【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执行周期任务是基于延时的,不会随系统时间变化引起执行变化。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章