代码之家  ›  专栏  ›  技术社区  ›  rouble

使用scheduleExecutorService启动和停止计时器

  •  5
  • rouble  · 技术社区  · 14 年前

    从我的读数看来,PraceDeXECutoService似乎是启动和停止Java中定时器的正确方法。

    我需要移植一些启动和停止计时器的代码。这不是周期计时器。此代码在启动计时器之前停止计时器。所以,实际上每次启动都是一次重新启动()。我正在寻找使用scheduleExecutorService进行此操作的正确方法。这就是我想到的。寻找对我所缺少的东西的评论和见解:

    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
    }
    

    蒂亚 卢布

    1 回复  |  直到 14 年前
        1
  •  3
  •   Scott Bale    14 年前

    这看起来有问题:

    private boolean startTimer() {
        // ......
            if (_TimerFuture != null) {
                _TimerFuture.cancel(false);
            }
    
            _TimerFuture = _Timer.schedule(new TimerPopTask(), 
                                           TIMER_IN_SECONDS, 
                                           TimeUnit.SECONDS);
        // ......
    }
    

    既然你要通过假取消,老 _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;
        }
    }
    

    然后我会保留 定时任务 而不是 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的引用(如我所描述),以及对当前计划的未来的引用,并尽最大努力取消它,并在可能的情况下释放线程,了解到它不能保证取消。

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