1

J'ai un service d'exécution qui exécute périodiquement un tas de tâches. Ils sont initialisés au démarrage et exécutés de temps en temps, jusqu'à présent.Démarrer des tâches dans ScheduledThreadPoolExecutor tôt/démarrer

Je voudrais maintenant ajouter une fonctionnalité pour démarrer l'exécution de ces tâches en fonction d'un événement.

J'ai trouvé la méthode decorateTask qui me permet de stocker les tâches que j'ai planifiées. Cependant, je ne sais pas comment je peux les faire courir? J'ai eu l'idée d'écraser la méthode Delayed dans RunnableScheduledFuture pour qu'elle renvoie 0 sur un événement prédéfini, mais je ne suis pas sûr que ce soit possible et que l'exécuteur se comporte si je le faisais?

Une autre idée serait de collecter toutes les tâches et ensuite de les soumettre pour exécution directement sur un événement. Je ne sais pas non plus comment cela se passerait.

Je ne peux pas simplement appeler les exécuter car ils fonctionneront dans le même thread.

J'espère que tout cela a du sens. Faites-moi savoir s'il y a quelque chose de flou.

public class EventBasedExecutor extends ScheduledThreadPoolExecutor implements EventBasedExecutorService { 
private static final Logger log = Logger.getLogger(EventBasedExecutor.class); 

private List<RunnableScheduledFuture<?>> workers = new ArrayList<>(); 

public EventBasedExecutor(int corePoolSize, ThreadFactory threadFactory) { 
    super(corePoolSize, threadFactory); 

} 

@Override 
protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) { 
    workers.add(task); 
    return super.decorateTask(runnable, task); 
} 

@Override 
public void executeEarly() { 
     // do something here to start the executors work 
} 

Répondre

1

Le moyen le plus simple et le plus facile à comprendre serait de collecter toutes les tâches et de les exécuter directement sur l'événement. J'ai juste besoin d'utiliser invokeAll de ExecutorService. Exemple de code peut aider:

public void handleSomeEvent(Event event) { 
    List<Task> tasksToRunOnEvent = getTasksToRunOnEvent(event); 
    List<Future<TaskResult> futures = executorService.invokeAll(tasksToRunOnEvent); 
    handleTaskResults(futures); 
} 
+0

Re-soumission des tâches passe par l'exécution/code horaire et crée une nouvelle tâche de l'ancien (appellera un nouveau décorez etc.) . Je ne peux pas dupliquer les tâches, j'ai besoin de démarrer les instances existantes pour que l'exécuteur ne puisse pas les exécuter en même temps (car elles sont déjà exécutées et doivent être soumises à nouveau). – pandaadb

+0

Qu'entendez-vous par «crée une nouvelle tâche par rapport à l'ancienne»? –

+0

Ce que je voulais dire, c'est qu'il prend la tâche et l'enveloppe dans une tâche différente. Cependant, l'écrasement de la méthode decorateTask le fait fonctionner correctement. Le runnable passé dans la méthode est la tâche précédente. En retournant celui-là, la tâche correcte peut être démarrée. – pandaadb

0

Je pris les conseils de tvelykyy et a joué un petit peu et est venu avec cette impl:

public class EventBasedExecutor extends ScheduledThreadPoolExecutor implements EventBasedExecutorService { 

    private List<RunnableScheduledFuture<?>> workers = new ArrayList<>(); 

    private int index; 

    public EventBasedExecutor(int corePoolSize) { 
     super(corePoolSize, new ThreadFactoryBuilder().setDaemon(true).setNameFormat("message-sender-%d").build()); 
    } 

    @Override 
    protected <V> RunnableScheduledFuture<V> decorateTask(Runnable runnable, RunnableScheduledFuture<V> task) { 
     if(!workers.contains(runnable)) { 
      workers.add(task); 
     } 
     return super.decorateTask(runnable, task); 
    } 

    @Override 
    public void executeEarly() { 
     if(index >= workers.size()) { 
      index = 0; 
     } 

     if(workers.size() == 0) { 
      return; 
     } 

     RunnableScheduledFuture<?> runnableScheduledFuture = workers.get(index); 
     index ++; 
     execute(runnableScheduledFuture); 
    } 

} 

Quelques mots:

workers.contains (Runnable) est vrai si vous soumettez à nouveau un RunnableScheduledFuture.

Vous devez le soumettre via l'exécution pour qu'il s'exécute dans le pool de threads (plutôt que d'appeler simplement run sur le runnable qui bloquera le thread actuel).

Je recommande aux runnables de mettre en place un dispositif de sécurité afin qu'ils ne puissent pas être exécutés deux fois en même temps (ce qui est important pour mon utilisation). Donc, fondamentalement:

if(isrunning()) return; 

Dans decorateTask - vous devez re-décorer le runnable. C'est parce que la mise en œuvre prendra le runnable enveloppé et le reprogrammer, s'il le faut. Puisque les anciennes runnables sont déjà planifiées, vous ne voulez pas les replanifier. L'appel de execute s'exécutera à travers la décoration et décorera le runnable programmé (qui sera généralement exécuté toutes les secondes ou quelques secondes) pour être un runnable non répétable. Cela signifie que votre tâche planifiée peut être exécutée une fois sans être replanifiée, tout en restant disponible en tant que runnable périodique dans l'exécuteur.

J'espère que cela prend tout son sens :)

Artur