Domanda

Il mio programma ha un componente - soprannominato Scheduler - che consente ad altri componenti di registrare i punti nel tempo in cui desiderano essere richiamati. Questo dovrebbe funzionare in modo molto simile al servizio cron di Unix, i. e. dici allo Scheduler " avvisami ogni dieci minuti ogni ora " ;.

Mi rendo conto che non ci sono callback reali in Java.

Ecco il mio approccio, c'è una biblioteca che già fa queste cose? Sentiti libero di suggerire anche miglioramenti.

Registra le chiamate ai pass Scheduler:

  • una specifica temporale contenente ora, minuto, secondo, anno mese, dom, dow, in cui ciascun elemento può non essere specificato, il che significa "eseguirlo ogni ora / minuto ecc." (proprio come i crontabs)
  • un oggetto contenente dati che indicheranno all'oggetto chiamante cosa fare quando viene notificato dallo Scheduler. Lo Scheduler non elabora questi dati, li archivia e li restituisce al momento della notifica.
  • un riferimento all'oggetto chiamante

All'avvio, o dopo una nuova richiesta di registrazione, l'Utilità di pianificazione inizia con un oggetto Calendario dell'ora di sistema corrente e verifica se nel database sono presenti voci corrispondenti a questo punto nel tempo. Se ci sono, vengono eseguiti e il processo ricomincia. In caso contrario, il tempo nell'oggetto Calendario viene incrementato di un secondo e l'impero viene ricontrollato. Questo si ripete fino a quando non c'è una o più voci corrispondenti. (Simulazione di eventi discreti)

Lo Scheduler ricorderà quindi quel timestamp, dormirà e si sveglierà ogni secondo per verificare se è già lì. Se capita di svegliarsi e il tempo è già passato, ricomincia, allo stesso modo se è arrivato il momento e i lavori sono stati eseguiti.


Modifica : grazie per avermi indicato Quartz. Sto cercando qualcosa di molto più piccolo, tuttavia.

È stato utile?

Soluzione

Se le tue esigenze sono semplici, considera l'utilizzo di java .util.Timer :

public class TimerDemo {

  public static void main(String[] args) {
    // non-daemon threads prevent termination of VM
    final boolean isDaemon = false;
    Timer timer = new Timer(isDaemon);

    final long threeSeconds = 3 * 1000;
    final long delay = 0;
    timer.schedule(new HelloTask(), delay, threeSeconds);

    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, 1);
    Date oneMinuteFromNow = calendar.getTime();

    timer.schedule(new KillTask(timer), oneMinuteFromNow);
  }

  static class HelloTask extends TimerTask {
    @Override
    public void run() {
      System.out.println("Hello");
    }
  }

  static class KillTask extends TimerTask {

    private final Timer timer;

    public KillTask(Timer timer) {
      this.timer = timer;
    }

    @Override
    public void run() {
      System.out.println("Cancelling timer");
      timer.cancel();
    }
  }

}

Come è stato annotato , il ExecutorService di < a href = "http://java.sun.com/javase/6/docs/api/java/util/concurrent/package-frame.html" rel = "nofollow noreferrer"> java.util.concurrent offre un'API più ricca se ne hai bisogno.

Altri suggerimenti

Ricerca Quartz

Se i tuoi oggetti conoscono esattamente i singoli punti nel tempo in cui desiderano essere eseguiti, puoi utilizzare un java.util.concurrent.ScheduledExecutorService . Quindi chiamano semplicemente:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
long timeToExecute = ... //read from DB? use CronTrigger?
long delayToExecution = timeToExecute - System.currentTimeMillis();
scheduler.schedule(aRunnable, delayToExecution, TimeUnit.MILLISECONDS);

Dovresti utilizzare Quartz solo se desideri che lo stesso programmatore gestisca funzionalità come "esegui ogni 5 secondi", o se desideri un comportamento complesso attorno alle esecuzioni mancate o alla persistenza di la traccia di controllo dell'esecuzione.

Puoi effettivamente riutilizzare banalmente la classe CronTrigger di Quartz per ottenere un "prossimo tempo di esecuzione". La classe è completamente autonoma e non dipende dall'essere invocata dal Quartz "contesto". Una volta che hai il prossimo tempo di esecuzione come Date o long , puoi semplicemente usare Java ScheduledExecutorService come sopra

Quartz è la grande e ovvia potenza in quest'area, ma ci sono alcune alternative da esplorare .

Cron4j è una libreria abbastanza decente, leggermente più leggera di Quartz. Fornisce una buona documentazione e farà ciò che desideri.

Probabilmente più interessante è se si desidera utilizzare una libreria che si adatta meglio alle librerie di concorrenza di Java (in particolare Executors ed ScheduledExecutors), quindi HA-JDBC ha un CronExecutorService , implementata dalla sua CronThreadPoolExecutor . Ora, interessante, ha una dipendenza da Quartz (per fornire la classe CronExpression), ma trovo che i due insieme funzionino meglio del solo Quartz da solo. Se non desideri dipendenze di grandi dimensioni, è facile estrarre la manciata di classi da Quartz e HA-JDBC che rendono possibile questo.

Dato che vuoi qualcosa di molto più piccolo (hai appena notato la tua modifica), prendi CronExpression da Quartz e le due classi HA-JDBC che ho menzionato sopra. Lo farà.

Consiglio vivamente cron4j (già menzionato) su Quartz, a meno che non sia assolutamente necessario alcune delle funzionalità più avanzate e complesse di Quartz. Cron4j si concentra bene su ciò che dovrebbe fare, ha una documentazione decente e non è una soluzione per lavello da cucina.

Scheduler al quarzo è generalmente raccomandato.

Non riesco a credere che java.util.Timer sia stato votato come risposta. Il quarzo è davvero una scelta molto migliore.

Un grande vantaggio del quarzo rispetto a java.util.Timer è che con il quarzo i lavori possono essere memorizzati nel db. Di conseguenza, un jvm può pianificare e un altro può essere eseguito. Inoltre (ovviamente) la richiesta sopravvive attraverso il riavvio di jvm.

  

Probabilmente più interessante è se si desidera utilizzare una libreria che si adatta meglio alle librerie di concorrenza di Java (in particolare Executors ed ScheduledExecutors), allora HA-JDBC ha un'interfaccia CronExecutorService, implementata dal suo CronThreadPoolExecutor. Ora, interessante, ha una dipendenza da Quartz (per fornire la classe CronExpression), ma trovo che i due insieme funzionino meglio del solo Quartz da solo. Se non desideri dipendenze di grandi dimensioni, è facile estrarre la manciata di classi da Quartz e HA-JDBC che rendono possibile questo.

Volevo solo dire che ho provato a estrarre queste classi e ha funzionato! Avevo bisogno di queste tre classi:

  • CronExpression (quarzo)
  • CronThreadPoolExecutor (ha-jdbc)
  • DaemonThreadFactory (ha-jdbc)

E ho dovuto fare solo queste piccole modifiche:

  • Rimozione del logger da CronThreadPoolExecutor (è stato creato ma mai utilizzato)
  • Sposta la costante YEAR_TO_GIVEUP_SCHEDULING_AT da CronTrigger a CronExpression

Ero entusiasta di non essermi bloccato in un groviglio di dipendenze. Complimenti agli autori della classe!

E ha funzionato come un campione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top