Java schedule 和 scheduleAtFixedRate 有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22486997/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 16:01:52  来源:igfitidea点击:

What is the difference between schedule and scheduleAtFixedRate?

java

提问by saplingPro

What is the difference between these 2 methods of Timerclass :

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

schedule(TimerTask task, long delay, long period)

and

scheduleAtFixedRate(TimerTask task, long delay, long period)

Documentationdoesn't make the difference between them clear.

文档并没有使它们之间的区别变得清晰。

采纳答案by JB Nizet

The documentation does explain the difference:

文档确实解释了区别:

schedule:

日程:

In fixed-delay execution, each execution is scheduled relative to the actual execution time of the previous execution. If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well.

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

So, suppose the delay is 5 seconds, and each task takes 2 seconds, you would get

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

TTWWWTTWWWTTWWWTT

where Tmeans 1 second for the task execution, and Wmeans 1 second waiting.

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

But now suppose that a long GC (represented by a G) happens and delays the second task, the third one will start 5 seconds after the start of the second one, as if the long GC didn't happen:

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

TTWWWGGTTWWWTTWWWTT

The third task starts 5 seconds after the second one.

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

scheduleAtFixedRate:

scheduleAtFixedRate:

In fixed-rate execution, each execution is scheduled relative to the scheduled execution time of the initial execution. If an execution is delayed for any reason (such as garbage collection or other background activity), two or more executions will occur in rapid succession to "catch up.".

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

So, with the same delay as above, and the same GC, you would get

因此,使用与上述相同的延迟和相同的 GC,您将得到

TTWWWGGTTWTTWWWTT

The third task task starts 3 seconds instead of 5 after the second one, to catch up.

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

回答by Eugene

Thanks @Nizet's answer, I have written a sample code for some people who want to practice and learn.

感谢@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");
            }
        }

    }
}

The task itself will repeat to take 2 seconds or 6 seconds. Let's see the result of each scenario.

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

When using timer.schedule(task, 0, 5000);, the output is TTWWWTTTTTTTTWWWTTTTTTTTWWWTTTTTTTT. As you can see, the timer follow the rules like below, wait till periodtime outs if task finishes in time, launch next task immediately if current task lasts more than period.

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

When using timer.scheduleAtFixedRate(task, 0, 5000);, the output is TTWWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTT. Things are a little different now. The javadoc

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

two or more executions will occur in rapid succession to "catch up."

两个或多个处决将快速连续发生以“赶上”。

takes effect here. As you can see, ignoring the first TTWWW, every two tasks will print TTTTTTTTWWand it lasts 10 seconds(two periods).

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

Let's dig into the source code of Timer.

让我们深入研究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);
}

As you can see, the periodis transferred to negative value in schedulemethod. Let's see what's the difference when scheduling it.

如您所见,periodschedule方法中被转换为负值。让我们看看调度时有什么不同。

The below code is in the mainloopof TimerThread,

下面的代码是在mainloopTimerThread

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);
           }
    }
}

It's where magic happens, for schedulemethod, the next task execution time is based on the currentTimewhich is calculated right before the this task runs. That means, every task's execution time only be related with previous task starts time.

这就是魔法发生的地方,对于schedule方法,下一个任务执行时间基于在currentTime此任务运行之前计算的时间。也就是说,每个任务的执行时间只与前一个任务的启动时间有关。