关于java.util.Timer timer定时器如何触发

关于java.util.Timer timer定时器如何触发

看看就好:在一次任务需求中,需求是每周固定时间点触发一次任务,当时用定时器实现,在需求发版后一直都是没问题的,突然某个星期本地的定时任务并没有触发,我便非常诧异害怕出现了什么BUG,便去测试定时器,发现不论怎么操作定时任务都没有触发。

步入正题:定时器是如何实现的?

实现定时器,便要先定义一个定时器,

	//定义定时器
    private java.util.Timer timer = new java.util.Timer(true);

此时我们还需要触发的定时任务:

//安排指定的任务在指定的时间开始进行重复的固定速率执行.
timer.schedule(TimerTask task,Date firstTime,long period)

其中schedule有很多重载方法,例如:

//安排在制定的时间执行指定的任务。
(1)Timer.schedule(TimerTask task,Date time)
//安排指定的任务在指定的时间开始进行重复的固定延迟执行.
(2)Timer.schedule(TimerTask task,Date firstTime ,long period)
//安排在指定延迟后执行指定的任务.
(3)Timer.schedule(TimerTask task,long delay)
//安排指定的任务从指定的延迟后开始进行重复的固定延迟执行.
(4)Timer.schedule(TimerTask task,long delay,long period)
//安排指定的任务在指定的时间开始进行重复的固定速率执行.
(5)Timer.scheduleAtFixedRate(TimerTask task,Date firstTime,long period)
//安排指定的任务在指定的延迟后开始进行重复的固定速率执行
(6)Timer.scheduleAtFixedRate(TimerTask task,long delay,long period)

解析定义定时器代码

	//定义定时器
    private java.util.Timer timer = new java.util.Timer(true);

此时我们进入Timer构造方法,

public Timer(String name, boolean isDaemon) {
        thread.setName(name);
        thread.setDaemon(isDaemon);
        thread.start();
    }

发现 thread 是Timer类的一个私有的成员变量

	private TaskQueue queue = new TaskQueue();
	
    private TimerThread thread = new TimerThread(queue);

我们再往下看,进入TimerThread类

class TimerThread extends Thread {
  
    boolean newTasksMayBeScheduled = true;

    private TaskQueue queue;

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

    public void run() {
        try {
            mainLoop();
        } finally {
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

    
    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; 

                    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类继承了Thread,上面thread.start();开启了线程则会执行 run方法,则会执行mainLoop方法,分析一下这个方法:
先是while (true)无限循环,然后定义了一个定时任务TimerTask,再定义了一个默认值为false的标识taskFired,然后同步判断queue是否为空,为空则等待。此时我们只定义了定时器,所以里面的任务列表时空的,线程被挂起。
此时执行到timer.schedule(TimerTask task,Date firstTime,long period)
我们进入schedule方法:

private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");

        synchronized(queue) {
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");

            synchronized(task.lock) {
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                        "Task already scheduled or cancelled");
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED;
            }

            queue.add(task);
            if (queue.getMin() == task)
                queue.notify();
        }
    }

其中将time时间赋值给任务的下一次执行时间nextExecutionTime,任务状态设为此任务还未被执行,将要执行,间隔赋给period,向任务队列添加任务queue.add(task)if (queue.getMin() == task)这个的意思是如果队列第一个任务是当前任务的话,唤醒队列。

唤醒队列后我们再来看mainLoop方法,定义了两个时间long currentTime, executionTime;一个是当前时间一个是执行时间。currentTime = System.currentTimeMillis();当前时间等于计算机系统时间,executionTime = task.nextExecutionTime;执行时间等于我们传入的下一次执行时间。if (taskFired = (executionTime<=currentTime)) {如果下一次执行时间小于当前时间,则更新下一次执行时间,task.run();run方法作为普通方法调用一次。如果下一次执行时间大于当前时间则queue.wait(executionTime - currentTime);等待相差时间执行,

揭晓答案:上面的源码中可以看到,此定时器所用来比较的时间是当前系统时间,也就是他会获取本地服务器的时间,当时我本地tomcat的系统时间错乱了🤪,导致本地调试时到我设定的时间时触发不了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值