我是靠谱客的博主 烂漫黑夜,最近开发中收集的这篇文章主要介绍定时任务之Timer定时器,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

      • 使用例子
      • 常用API
      • 工作原理
      • 总结

使用例子

package com.myself.study;

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

public class TimerDemo {

    public static void main(String[] args) {

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("timer执行业务逻辑...");
            }
        }, 1000, 1000);

        Timer timer1 = new Timer();
        timer1.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("timer1执行业务逻辑...");
            }
        }, 1000, 1000);
    }
}

常用API

// delay延时之后执行task,只执行一次
public void schedule(TimerTask task, long delay);
// 当时间到达time指定的时间时执行task,只执行一次
public void schedule(TimerTask task, Date time);
// delay延时之后执行一次task,然后每隔period时间执行一次task
public void schedule(TimerTask task, long delay, long period);
// 当时间到达firstTime指定的时间时执行一次task,然后每隔period时间执行一次task
public void schedule(TimerTask task, Date firstTime, long period);
// delay延时之后执行一次task,然后每隔period时间执行一次task
public void scheduleAtFixedRate(TimerTask task, long delay, long period);
// 当时间到达firstTime指定的时间时执行一次task,然后每隔period时间执行一次task
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period);

Timer常用的API就是上述6个以schedule开头的方法,大体分为单次调度和周期性调度两类。其中对于周期性调度的方法需要特别说明的是,schedule和scheduleAtFixedRate正常情况下功能无差,但是在特殊情况下就会有部分差异,那就是当正在被调度的任务执行耗时很长,超过了period指定的时间间隔,schedule调度会在(任务结束时间 + period时间间隔)点再调度下次任务,而scheduleAtFixedRate调度则会尽量按照period对应的原始时间点调度下次任务。
比如,有一个任务原定周期执行间隔10s,0s处开始执行一次任务,因某些原因耗时13s,那么schedule和scheduleAtFixedRate运行时间点分别如下:
schedule:0s 13s 23s 33s …
scheduleAtFixedRate: 0s 13s 20s 30s …

工作原理

Timer对象内部持有一个TimerThread工作线程以及一个TaskQueue工作队列,调度任务首先通过Timer的schdule*相关方法提交到TaskQueue任务队列中,然后TimerThread工作线程业务逻辑中,通过while(true)死循环不断从TaskQueue队列中每次取出一个任务调用其run方法执行,所以说Timer定时器调度任务是单线程的,如果正在被调度的任务耗时太长就会阻塞TaskQueue队列中其他任务的执行。

class TimerThread extends Thread {
    /**
     * This flag is set to false by the reaper to inform us that there
     * are no more live references to our Timer object.  Once this flag
     * is true and there are no more tasks in our queue, there is no
     * work left for us to do, so we terminate gracefully.  Note that
     * this field is protected by queue's monitor!
     */
    boolean newTasksMayBeScheduled = true;

    /**
     * Our Timer's queue.  We store this reference in preference to
     * a reference to the Timer so the reference graph remains acyclic.
     * Otherwise, the Timer would never be garbage-collected and this
     * thread would never go away.
     */
    private TaskQueue queue;

    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

    /**
     * The main timer loop.  (See class comment.)
     */
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
                    if (queue.isEmpty())
                        break; // Queue is empty and will forever remain; die

                    // Queue nonempty; look at first evt and do the right thing
                    long currentTime, executionTime;
                    task = queue.getMin();
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  // No action required, poll queue again
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        if (taskFired = (executionTime<=currentTime)) {
                            if (task.period == 0) { // Non-repeating, remove
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // Repeating task, reschedule
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }
}

主要逻辑就位于TimerThread类中,可以看到TimerThread的mainLoop方法中,主逻辑被while(true)死循环包裹,从队列中依次取出一个任务执行,这个主逻辑虽然被try/cache包裹,但是只会捕获InterruptedException 异常,一旦出现其他异常,mainLoop方法就会退出,接着TimerThread的run方法也会退出,这也就意味着这个线程因异常而停止运行了,那么Timer定时器也就没有用了。

总结

  1. Timer定时器单线程执行,执行时间长的任务会阻塞其他任务,即使已经到了调度时间。
  2. Timer定时器调度的任务如果抛出异常,TimerThread线程就会退出,Timer定时器也就没用了。
  3. Timer定时器比较古老,现在用的比较少了,权当了解。

最后

以上就是烂漫黑夜为你收集整理的定时任务之Timer定时器的全部内容,希望文章能够帮你解决定时任务之Timer定时器所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部