Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between schedule and scheduleAtFixedRate?

Tags:

java

What is the difference between these 2 methods of Timer class :

schedule(TimerTask task, long delay, long period)

and

scheduleAtFixedRate(TimerTask task, long delay, long period)

Documentation doesn't make the difference between them clear.

like image 393
saplingPro Avatar asked Mar 18 '14 17:03

saplingPro


2 Answers

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

TTWWWTTWWWTTWWWTT

where T means 1 second for the task execution, and W means 1 second waiting.

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:

TTWWWGGTTWWWTTWWWTT

The third task starts 5 seconds after the second one.

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

TTWWWGGTTWTTWWWTT

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

like image 120
JB Nizet Avatar answered Nov 09 '22 09:11

JB Nizet


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

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.

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

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

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 TTTTTTTTWW and it lasts 10 seconds(two periods).

Let's dig into the source code of 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 period is transferred to negative value in schedule method. Let's see what's the difference when scheduling it.

The below code is in the mainloop of 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);
           }
    }
}

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

like image 2
Eugene Avatar answered Nov 09 '22 08:11

Eugene