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