使用ScheduledExecutorService启动和停止计时器

rou*_*ble 5 java multithreading design-patterns timer executorservice

从我的读数来看,似乎ScheduledExecutorService是在Java中启动和停止计时器的正确方法.

我需要移植一些启动和停止计时器的代码.这不是周期性计时器.此代码在启动之前停止计时器.所以,实际上每次启动都是一次重启().我正在寻找使用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
}
Run Code Online (Sandbox Code Playgroud)

tia,卢布

Sco*_*ale 3

这看起来像一个问题:

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

        _TimerFuture = _Timer.schedule(new TimerPopTask(), 
                                       TIMER_IN_SECONDS, 
                                       TimeUnit.SECONDS);
    // ......
}
Run Code Online (Sandbox Code Playgroud)

由于您传递 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

那么我将保留 的实例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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

private ScheduledExecutorService _Timer = Executors.newScheduledThreadPool(5);
Run Code Online (Sandbox Code Playgroud)

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

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