Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么TimerTask不能与新的Timer对象一起重用?_Java_Timer - Fatal编程技术网

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,实际上在各个方面都更好(应该改用)