5

Dalle mie letture, sembra che ScheduledExecutorService sia il modo giusto per avviare e arrestare i timer in Java.utilizzando ScheduledExecutorService per avviare e arrestare il timer

Ho bisogno di portare un codice che avvia e ferma un timer. Questo non è un timer periodico. Questo codice, arresta il timer prima di avviarlo. Quindi, effettivamente ogni avvio è davvero un riavvio(). Sto cercando il modo giusto per farlo utilizzando ScheduledExecutorService. Ecco cosa mi è venuto in mente. In cerca di commenti e approfondimenti sulle cose che mi manca:

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, rublo

+0

Codice come scritto non verrà compilato: _batchTimer in startTimer() non è dichiarato da nessuna parte. Sarebbe utile se potessi approfondire in dettaglio i comportamenti previsti: quali sono i valori di ritorno di startTimer e stopTimer? Perché vuoi che start/stopTimer blocchi potenzialmente le chiamate? – Sbodd

+0

@Sbodd, quando vado ad avviare un timer, se c'è già un timer in esecuzione, voglio interromperlo, in modo da non avere due timer che scoppino allo stesso tempo. Penso che invece di get() lì, ho solo bisogno di chiamare cancel() sull'istanza futura. – rouble

risposta

3

Questo appare come un problema:

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

     _TimerFuture = _Timer.schedule(new TimerPopTask(), 
             TIMER_IN_SECONDS, 
             TimeUnit.SECONDS); 
    // ...... 
} 

Dal momento che si sta passando un falso di annullare, il il vecchio _TimerFuture potrebbe non essere annullato se l'attività è già in esecuzione. Viene creato comunque uno nuovo (ma non verrà eseguito contemporaneamente perché il tuo ExecutorService ha una dimensione fissa del pool di thread pari a 1). In ogni caso, ciò non sembra il comportamento desiderato per riavviare un timer quando viene chiamato startTimer().

Vorrei rearchitect un po '. Vorrei fare l'istanza TimerPopTask essere la cosa che si "annulla", e vorrei lasciare la ScheduledFutures solo una volta che vengono creati:

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

quindi vorrei mantenere l'istanza di TimerPopTask piuttosto che l'istanza di ScheduledFuture e riorganizzare startTimer metodo di questa convenzione:

(. metodo di modifica simile a stopTimer())
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; 
    } 
} 

si consiglia di alzare il numero di thread se si prevede veramente nee ding a 'restart' il timer prima che il timer attuale scade:

private ScheduledExecutorService _Timer = Executors.newScheduledThreadPool(5); 

si consiglia di andare con un approccio ibrido, mantenendo i riferimenti sia alla TimerPopTask corrente come ho descritto e anche per lo ScheduledFuture corrente e fare il miglior sforzo per cancellarlo e liberare il thread se possibile, capendo che non è garantito l'annullamento.

(Nota:.. Tutto questo presuppone startTimer() e stopTimer() chiamate di metodo sono limitati ad un unico filo conduttore, e solo i TimerPopTask casi sono condivisi tra i thread, altrimenti avrete bisogno di ulteriori garanzie)

+0

buon punto. Lascia che ti chieda questo, diciamo che era in corso un compito. Viene creata una nuova attività e pianificata per l'esecuzione di TIMER_IN_SECONDS secondi dopo. Quando inizia questa volta (i secondi di TIMER_IN_SECONDS)? Inizia proprio allora, quando schedule() viene chiamato O inizia quando c'è un thread libero nel pool di thread? – rouble

+0

Il ritardo TIMER_IN_SECONDS inizia 'ora' a destra quando viene chiamato schedule().L'attività è garantita per iniziare non prima del ritardo, tuttavia, può iniziare in un secondo momento (a seconda della parte sulla disponibilità del thread). Da javadoc per ScheduledThreadPoolExecutor: "Le attività ritardate vengono eseguite non prima di essere abilitate, ma senza garanzie in tempo reale su quando, dopo essere state abilitate, inizieranno." –