36

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.

saplingPro
  • 20,769
  • 53
  • 137
  • 195

2 Answers2

62

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.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thanks, I already understood from other answers the concept, but the `TWG` example was very clear. Question: if I have longer tasks, for example 7 secs and 4s, with `scheduleAtFixedRate` will I have something like `T1T1T1T1T1.T1T1T2T2T2.T2T3T3WW` ? – Enrichman Sep 16 '15 at 15:53
  • In `scheduleAtFixedRate()`, if a task takes long time to complete. Suppose each task takes roughly 2secs to complete and there is a delay of 3secs between each task. So in normal execution it will be `TTWTTWTTW...` But for some reason, task-2 takes 5 secs to complete, when will task-3 start? Will it start at 6th sec as before or at 8th sec waiting for task-2 to complete – warrior107 Oct 12 '16 at 10:30
2

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.

Eugene
  • 10,627
  • 5
  • 49
  • 67