Java 为什么TimerTask不能与新的Timer对象一起重用?
Java 为什么TimerTask不能与新的Timer对象一起重用?,java,timer,Java,Timer,Timer(java.util.Timer)文档将cancel方法描述为影响计时器的方法,并声明在取消后无法使用计时器。因此,我实例化了一个新的计时器。为什么不让我在本例中重复使用参数task0?我甚至没有调用purge,这被描述为使任务符合GC条件。除非有其他解释,否则我声明Timer类不应影响仅作为其参数的TimerTask对象 import java.util.Timer; import java.util.TimerTask; public class Tester { pu
Timer
(java.util.Timer
)文档将cancel
方法描述为影响计时器的方法,并声明在取消后无法使用计时器。因此,我实例化了一个新的计时器。为什么不让我在本例中重复使用参数task0
?我甚至没有调用purge
,这被描述为使任务符合GC条件。除非有其他解释,否则我声明Timer
类不应影响仅作为其参数的TimerTask
对象
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");
}
}
允许这样做很容易出错,因为当另一个计时器再次调度时,task0
可能仍在运行。(请注意,cancel()
不会终止任务。)
请注意,如果task0
由单个计时器管理,则同一任务永远不会与其自身同时执行(无论是以固定延迟还是固定速率执行)
如果您真的想要这样的行为,解决方法是让task0
和task1
包装一个公共对象:
class Task extends TimerTask {
Runnable runnable;
Task(Runnable runnable) {
this.runnable = runnable;
}
@Override
public void run() {
runnable.run();
}
}
然后像这样执行它:
// "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();
看一看:
TimerTask类只是Runnable的一个精简扩展,它跟踪一些关于调度的元数据(即:下一次执行时间)。但是,如果您在两个计时器上调度它,那么仍然只有一个next execution字段,因此一个计时器将覆盖另一个计时器的下一个执行时间,这几乎肯定不是您想要的,因此它会跟踪之前已调度的时间,并在timer中引发异常
如果允许这样做,您将获得相当意外的行为。如果计时器任务希望在同一个计时器中重新安排自己的时间,这也是一个问题,这次可能会有不同的延迟。
例如,这将允许我实现指数退避算法(重试任务,延迟呈指数增长)
似乎使用ScheduledExecutorService可以最轻松地实现这种具有可变延迟的计时器任务,因为该类不构成此类限制。这是我所听过的最倒行逆施的推理。如果该类的契约允许它由多个计时器调度,则显然不会有一个field表示下一次执行时间。@aioobe-可能,但这是对“我声明Timer类不应影响仅作为其参数的TimerTask对象”的唯一实际回答也许不应该,但是Timer/TimerTask的实现不是很好。你不应该知道TimerTask的任何特殊情况-作为用户,你不会使用任何状态,它只是用于计时器。所以Timer应该管理它-你应该能够传递一个原始的runnable,并让它保持状态。比如ScheduledThreadPoolExecutor,实际上在各个方面都更好(应该改用)