可重置的Java计时器

时间:2020-03-05 18:44:18  来源:igfitidea点击:

我想在java.utils.Timer中设置一个可重置时间的java.utils.Timer,我需要设置一个一次性事件以在X秒内发生。如果在创建计时器的时间到X秒之间没有任何反应,则该事件将正常发生。

但是,如果在X秒过去之前,我决定该事件应在Y秒之后发生,那么我希望能够告诉计时器重置其时间,以便该事件在Y秒内发生。
例如。计时器应该能够执行以下操作:

Timer timer = new Timer();  
timer.schedule(timerTask, 5000); //Timer starts in 5000 ms (X)

//At some point between 0 and 5000 ms...  
setNewTime(timer, 8000);  //timerTask will fire in 8000ms from NOW (Y).

我看不到使用utils计时器执行此操作的方法,就像我们调用cancel()一样,我们无法再次安排它。

我接近复制此行为的唯一方法是使用javax.swing.Timer,它涉及停止原始计时器,并创建一个新计时器。 IE。:

timer.stop();
timer = new Timer(8000, ActionListener);
timer.start();

有没有更简单的方法??

解决方案

回答

我们需要安排重复任务吗?在这种情况下,我建议我们考虑使用Quartz。

回答

我不认为可以使用" Timer / TimerTask"来做到这一点,但是取决于我们要实现的目标,我们可能会对使用" java.util.concurrent.ScheduledThreadPoolExecutor"感到满意。

回答

根据Timer文档,从Java 1.5开始,我们应该更喜欢ScheduledThreadPoolExecutor。 (为方便使用,我们可能希望使用Executors..newSingleThreadScheduledExecutor()创建该执行器;它创建的东西类似于" Timer"。)

很棒的事情是,当我们计划一个任务时(通过调用schedule()),它将返回一个" ScheduledFuture"对象。我们可以使用它来取消计划的任务。然后,我们可以在不同的触发时间下提交新任务。

ETA:链接到的" Timer"文档没有提及任何有关" ScheduledThreadPoolExecutor"的信息,但是OpenJDK版本却说:

Java 5.0 introduced the java.util.concurrent package and
  one of the concurrency utilities therein is the 
  ScheduledThreadPoolExecutor which is a thread pool for repeatedly
  executing tasks at a given rate or delay.  It is effectively a more
  versatile replacement for the Timer/TimerTask
  combination, as it allows multiple service threads, accepts various
  time units, and doesn't require subclassing TimerTask (just
  implement Runnable).  Configuring
  ScheduledThreadPoolExecutor with one thread makes it equivalent to
  Timer.

回答

如果"计时器"仅要执行一项任务,则建议将其子类化:

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

public class ReschedulableTimer extends Timer
{
    private Runnable  task;
    private TimerTask timerTask;

    public void schedule(Runnable runnable, long delay)
    {
        task = runnable;
        timerTask = new TimerTask()
        {
            @Override
            public void run()
            {
                task.run();
            }
        };
        this.schedule(timerTask, delay);
    }

    public void reschedule(long delay)
    {
        timerTask.cancel();
        timerTask = new TimerTask()
        {
            @Override
            public void run()
            {
                task.run();
            }
        };
        this.schedule(timerTask, delay);
    }
}

我们将需要处理代码以添加对滥用的检查,但是它应该实现我们想要的。 ScheduledThreadPoolExecutor似乎也没有内置对重新安排现有任务的支持,但是类似的方法也应该在那里工作。

回答

这就是我正在尝试的。我有一个使用TimerTask每60秒轮询一次数据库的类。

在我的主类中,我保留了Timer的实例以及TimerTask的本地子类的实例。主类有一种方法来设置轮询间隔(例如从60到30)。在其中,我取消了TimerTask(这是我的子类,在这里我重写了cancel()方法以进行一些清理,但这没关系),然后将其设为null。我重新创建它的一个新实例,并在现有计时器中以新间隔安排新实例。

由于Timer本身并未被取消,因此它使用的线程保持活动状态(内部的其他TimerTask也将保持活动状态),并且旧的TimerTask被替换为新的,恰好是相同的,但是VIRGIN(因为旧的已执行或者已调度,则不再是VIRGIN(调度所需)。

当我想关闭整个计时器时,我取消了TimerTask并将其设为空(与更改计时时所做的相同,再次用于清理TimerTask子类中的资源),然后取消了Timer本身并将其设为空。