Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a TimerTask not reusable with a new Timer object?

Tags:

java

timer

The Timer (java.util.Timer) doc describes the cancel method as one that affects the Timer and it states that the timer cannot be used after cancellation. So I instantiate a new Timer. Why will it not let me re-use the argument task0 in this example? I'm not even invoking purge which is described as making tasks GC-eligible. Until it might be explained to be otherwise, I claim Timer class should not affect a TimerTask object that is merely an argument to it.

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

public class Tester {

    public static void main(String[] args) throws InterruptedException {
        long delay = 3000L;

        Timer timer0 = new Timer();
        Task task0 = new Task();
        timer0.schedule(task0, delay);
        timer0.cancel();

        Timer timer1 = new Timer();
        timer1.schedule(task0, delay); // throws an exception if we use task0

        Thread.sleep(5000);
        timer1.cancel();
    }
}

class Task extends TimerTask {
    Task() {
    }
    @Override
    public void run() {
        System.out.println("task was invoked");
    }
}
like image 378
H2ONaCl Avatar asked Mar 31 '12 10:03

H2ONaCl


Video Answer


2 Answers

Allowing this would be error prone, since task0 could still be running when scheduled again by another timer. (Note that cancel() does not terminate the task.)

Note that if task0 is managed by a single Timer, the same task will never be executed concurrently with itself (regardless if it is executed with fixed-delay or with fixed-rate).

If you really want such behavior, the work around would be to let task0 and a task1 wrap a common object:

class Task extends TimerTask {
    Runnable runnable;
    Task(Runnable runnable) {
        this.runnable = runnable;
    }
    @Override
    public void run() {
        runnable.run();
    }
}

And then execute it like this:

// "Wrapped" (and thus shared) by task0 and task1 below.
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("task was invoked");
    }
}



Timer timer0 = new Timer();
Task task0 = new Task(runnable);
timer0.schedule(task0, delay);
timer0.cancel();


Task task1 = new Task(runnable);
Timer timer1 = new Timer();
timer1.schedule(task1, delay); // throws an exception if we use task0

Thread.sleep(5000);
timer1.cancel();
like image 86
aioobe Avatar answered Oct 02 '22 23:10

aioobe


Take a look:

http://www.docjar.com/html/api/java/util/TimerTask.java.html

http://www.docjar.com/html/api/java/util/Timer.java.html

The TimerTask class is just a thin extension of Runnable which tracks a bit of metadata about scheduling (namely: next execution time). But, if you schedule it on two timers, there's still only one next execution field, so one timer would overwrite the next execution time of the other, which is almost certainly not what you want, so it tracks that it's been scheduled before, and throws an exception in Timer, instead.

If it allowed this, you'd get rather unexpected behavior.

like image 24
James Avatar answered Oct 03 '22 01:10

James