использование ScheduledExecutorService для запуска и остановки таймера - PullRequest
5 голосов
/ 23 июня 2010

Из моих чтений кажется, что 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
}

ТИА, рубль

1 Ответ

3 голосов
/ 24 июня 2010

Это выглядит как проблема:

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;
    }
}

, тогда я бы скорее сохранил экземпляр 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 совместно используются потоками. В противном случае вам потребуются дополнительные меры защиты.)

...