schedule和scheduleAtFixedRate有什么区别?

时间:2014-03-18 17:41:57

标签: java

Timer类的这两种方法有什么区别:

schedule(TimerTask task, long delay, long period)

scheduleAtFixedRate(TimerTask task, long delay, long period)

Documentation并没有明确区别它们。

2 个答案:

答案 0 :(得分:47)

文档确实解释了区别:

时间表:

  

在固定延迟执行中,每次执行都是相对于上一次执行的实际执行时间进行调度的。如果执行因任何原因(例如垃圾收集或其他后台活动)而延迟,则后续执行也将延迟。

所以,假设延迟是5秒,每个任务需要2秒,你会得到

TTWWWTTWWWTTWWWTT

其中T表示任务执行时间为1秒,W表示等待1秒钟。

但是现在假设一个长GC(由G表示)发生并延迟第二个任务,第三个任务将在第二个任务开始后5秒开始,好像长GC没有发生:

TTWWWGGTTWWWTTWWWTT

第三项任务在第二项任务开始后5秒开始。

scheduleAtFixedRate:

  

在固定速率执行中,每次执行都是相对于初始执行的预定执行时间进行调度的。如果执行因任何原因(例如垃圾收集或其他后台活动)而延迟,则会快速连续执行两次或更多次执行以“赶上”。

因此,与上面相同的延迟和相同的GC,你会得到

TTWWWGGTTWTTWWWTT

第三个任务任务开始3秒而不是第二个任务之后的第5个任务任务,以赶上。

答案 1 :(得分:1)

谢谢@Nizet的回答,我为一些想练习和学习的人写了一个示例代码。

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

public class TimerTest {

    public static void main(String args[]){
        TimerTest.DelayTask task = new DelayTask();
        Timer timer = new Timer();
        /**
         * Use schedule or scheduletAtFixedrate and check the printed result
         */
        timer.schedule(task, 0, 5000);
        //timer.scheduleAtFixedRate(task, 0, 5000);
    }

    public static boolean stop = false;

    public static void delayOneSec(String status){
        try{
            System.out.print(status);
            Thread.sleep(1000);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    static class DelayTask extends TimerTask{
        int count = 2;

        @Override
        public void run() {
            // TODO Auto-generated method stub
            stop = true;
            for(int i = 0; i < count; i++){
                TimerTest.delayOneSec("T");
            }
            if(count == 2){
                count = 6;
            }else{
                count = 2;
            }
            stop = false;
            new PrintW().start();
        }
    }

    static class PrintW extends Thread{
        @Override
        public void run(){
            while(!stop){
                TimerTest.delayOneSec("W");
            }
        }

    }
}

任务本身将重复花费2秒或6秒。让我们看看每个场景的结果。

使用timer.schedule(task, 0, 5000);时,输出为TTWWWTTTTTTTTWWWTTTTTTTTWWWTTTTTTTT。如您所见,计时器遵循以下规则,如果任务及时完成,则等待period超时,如果当前任务持续时间超过period,则立即启动下一个任务。

使用timer.scheduleAtFixedRate(task, 0, 5000);时,输出为TTWWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTT。现在情况有点不同了。 javadoc

  

两次或多次执行将快速连续“赶上”。

在这里生效。如您所见,忽略第一个TTWWW,每两个任务将打印TTTTTTTTWW并持续10秒(两个句点)。

让我们深入研究Timer

的源代码
public void schedule(TimerTask task, Date firstTime, long period) {
    if (period <= 0)
        throw new IllegalArgumentException("Non-positive period.");
    sched(task, firstTime.getTime(), -period);
}


public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
    if (delay < 0)
        throw new IllegalArgumentException("Negative delay.");
    if (period <= 0)
        throw new IllegalArgumentException("Non-positive period.");
    sched(task, System.currentTimeMillis()+delay, period);
}

如您所见,periodschedule方法中转为负值。让我们看看在安排它时有什么不同。

以下代码位于mainloop的{​​{1}},

TimerThread

这就是魔术发生的地方,对于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); } } } 方法,下一个任务执行时间基于在此任务运行之前计算的schedule。这意味着,每个任务的执行时间仅与先前任务的开始时间相关。