【问题标题】:using ScheduledExecutorService to start and stop timer使用 ScheduledExecutorService 启动和停止计时器
【发布时间】:2010-06-23 17:38:18
【问题描述】:

根据我的阅读,ScheduledExecutorService 似乎是在 Java 中启动和停止计时器的正确方法。

我需要移植一些启动和停止计时器的代码。这不是一个周期性定时器。此代码在启动计时器之前停止计时器。因此,实际上每次启动实际上都是一个restart()。我正在寻找使用 ScheduledExecutorService 执行此操作的正确方法。这是我想出的。寻找 cmets 并了解我所缺少的东西:

ScheduledExecutorService _Timer = Executors.newScheduledThreadPool(1);
ScheduledFuture<?> _TimerFuture = null;

private boolean startTimer() {
    try {
        if (_TimerFuture != null) {
            //cancel execution of the future task (TimerPopTask())
            //If task is already running, do not interrupt it.
            _TimerFuture.cancel(false);
        }

        _TimerFuture = _Timer.schedule(new TimerPopTask(), 
                                       TIMER_IN_SECONDS, 
                                       TimeUnit.SECONDS);
        return true;
    } catch (Exception e) {
        return false;
    }
}

private boolean stopTimer() {
    try {
        if (_TimerFuture != null) {
            //cancel execution of the future task (TimerPopTask())
            //If task is already running, interrupt it here.
            _TimerFuture.cancel(true);
        }

        return true;
    } catch (Exception e) {
        return false;
    }
}

private class TimerPopTask implements Runnable  {  
    public void run ()   {  
        TimerPopped();
    }  
}

public void TimerPopped () {
    //Do Something
}

tia, 卢布

【问题讨论】:

  • 编写的代码无法编译:startTimer() 中的 _batchTimer 未在任何地方声明。如果您可以更详细地了解预期行为会很有帮助:startTimer 和 stopTimer 的返回值是什么?为什么你想让 start/stopTimer 潜在地阻塞调用?
  • @Sbodd,当我开始一个计时器时,如果已经有一个计时器正在运行,我想停止它,这样我就不会同时弹出两个计时器。我认为不是那里的 get(),我只需要在未来的实例上调用 cancel()。

标签: java multithreading design-patterns timer executorservice


【解决方案1】:

这看起来是个问题:

private boolean startTimer() {
    // ......
        if (_TimerFuture != null) {
            _TimerFuture.cancel(false);
        }

        _TimerFuture = _Timer.schedule(new TimerPopTask(), 
                                       TIMER_IN_SECONDS, 
                                       TimeUnit.SECONDS);
    // ......
}

由于您传递一个 false 来取消,如果任务已经在运行,旧的 _TimerFuture 可能不会被取消。无论如何都会创建一个新的(但它不会同时运行,因为您的 ExecutorService 具有固定的线程池大小为 1)。无论如何,在调用 startTimer() 时,这听起来不像您想要的重启计时器的行为。

我会重新架构一下。我会让TimerPopTask 实例成为您“取消”的东西,并且一旦创建ScheduledFutures,我将不理会它们:

private class TimerPopTask implements Runnable  {
    //volatile for thread-safety
    private volatile boolean isActive = true;  
    public void run ()   {  
        if (isActive){
            TimerPopped();
        }
    }  
    public void deactivate(){
        isActive = false;
    }
}

那么我将保留TimerPopTask 的实例而不是ScheduledFuture 的实例并因此重新排列startTimer 方法:

private TimerPopTask timerPopTask;

private boolean startTimer() {
    try {
        if (timerPopTask != null) {
            timerPopTask.deactivate();
        }

        timerPopTask = new TimerPopTask();
        _Timer.schedule(timerPopTask, 
                        TIMER_IN_SECONDS, 
                        TimeUnit.SECONDS);
        return true;
    } catch (Exception e) {
        return false;
    }
}

(与 stopTimer() 方法类似的修改。)

如果您确实预计需要在当前计时器到期之前“重新启动”计时器,您可能希望增加线程数:

private ScheduledExecutorService _Timer = Executors.newScheduledThreadPool(5);

您可能希望采用混合方法,同时保留对我描述的当前 TimerPopTask 和当前 ScheduledFuture 的引用,并尽最大努力取消它并释放线程(如果可能),了解它不能保证取消。

(注意:这一切都假设 startTimer() 和 stopTimer() 方法调用仅限于单个主线程,并且只有 TimerPopTask 实例在线程之间共享。否则您将需要额外的保护措施。)

【讨论】:

  • 好点。让我问你这个,说一个任务正在运行。创建了一个新任务,并计划在 TIMER_IN_SECONDS 秒后运行。这个时间(TIMER_IN_SECONDS 秒)什么时候开始?它是在调用 schedule() 时立即开始,还是在线程池中有空闲线程时开始?
  • TIMER_IN_SECONDS 延迟在调用 schedule() 时立即开始。保证任务不早于延迟开始,但是,它可能会稍后开始(部分取决于线程可用性)。来自 ScheduledThreadPoolExecutor 的 javadoc:“延迟的任务在它们被启用之前执行,但没有任何实时保证在它们被启用后何时开始。”
猜你喜欢
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-03
  • 1970-01-01
相关资源
最近更新 更多