5

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

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

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, 卢布

4

1 回答 1

3

这看起来像一个问题:

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实例在线程之间共享。否则您将需要额外的保护措施。)

于 2010-06-23T20:42:20.253 回答