2010-06-23 193 views
5

從我的閱讀中,似乎ScheduledExecutorService是在Java中啓動和停止計時器的正確方法。使用ScheduledExecutorService啓動和停止計時器

我需要移植一些啓動和停止計時器的代碼。這不是週期性的計時器。該代碼在啓動之前停止計時器。所以,實際上每個開始都是重新啓動()。我正在尋找使用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, 盧布

+0

代碼因爲寫入不會編譯:startTimer()中的_batchTimer沒有在任何地方聲明。 如果您可以詳細瞭解預期行爲,那麼這會有所幫助:startTimer和stopTimer的返回值是多少?爲什麼你想讓start/stopTimer成爲潛在的阻塞呼叫? – Sbodd 2010-06-23 18:02:00

+0

@Sbodd,當我開始一個計時器時,如果已經有一個計時器正在運行,我想停止它,這樣我就沒有兩個計時器同時彈出。我認爲,而不是get()那裏,我只需要在未來的實例上調用cancel()。 – rouble 2010-06-23 18:09:11

回答

3

這看起來像一個問題:

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

爲我描述,也對當前ScheduledFuture您可能想要去的混合方式,保持引用到雙方當前TimerPopTask並作出最佳努力取消它並儘可能釋放線索,理解它不能保證取消。

(注意:這一切假設startTimer所()和stopTimer()方法調用被限制在單一的主線程,只有TimerPopTask實例線程之間共享,否則你將需要額外的保障措施)

+0

好點。讓我問你這個,說一個任務正在運行。創建一個新任務,並計劃在幾秒後運行TIMER_IN_SECONDS。此時間(TIMER_IN_SECONDS秒)何時開始?當它調用schedule()時它是否開始正確,或者當線程池中有空閒線程時它是否開始? – rouble 2010-06-24 03:56:51

+0

當schedule()被調用時,TIMER_IN_SECONDS延遲立即開始'now'。該任務保證在延遲後立即開始,但可能會在稍後開始(部分取決於線程可用性)。從ScheduledThreadPoolExecutor的javadoc中:「延遲任務在啓用後立即執行,但沒有實時保證何時啓用它們將啓動。」 – 2010-06-24 14:42:36