2016-08-24 1 views
1

J'interroge une API pour créer un rapport et je souhaite charger ce rapport 5 minutes plus tard. Je veux utiliser un ScheduledExecutorService pour cela. Comme je ne veux pas que l'Executor bloque mon fil, je crée un nouveau fil pour cela, mais je ne sais pas si c'est la bonne façon de le faire. Voici mon code:Exécution du programme dans un nouveau fil de discussion

Thread thread = new Thread() { 
     public void run() { 
      log.info("Starting..."); 
      new RequestReport().runScheduledTask(requestId); 
     } 
    }; 
thread.start(); 

private void runScheduledTask(String requestId) { 
    log.info("Starting five-minute countdown now..."); 
    ScheduledFuture<?> countdown = scheduler.schedule(() -> { 
     try { 
      new GetReportList().run(requestId); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    }, 5, TimeUnit.MINUTES); 

    try { 
     countdown.get(); 
    } catch (InterruptedException | ExecutionException e) { 
     log.info("catched Exception"); 
     e.printStackTrace(); 
    } 
    scheduler.shutdown(); 
} 

Y at-il une meilleure façon d'exécuter une fonction 5 minutes après l'autre? Est-ce que c'est comme ça que je fais ça? Que devrais-je changer?

BTW, j'utilise le printemps - y a-t-il quelque chose qui pourrait améliorer cela?

+0

Si vous utilisez Spring, vous pouvez également utiliser '@ Scheduled' [annotation] (https://spring.io/guides/gs/scheduling-tasks/) pour planifier une invocation de méthode. –

+0

Mais que puis-je faire de façon dynamique? Je dois exécuter la tâche cinq minutes après l'autre - l'heure d'exécution n'est pas connue au démarrage du programme. @SurajBajaj –

+0

@ J.Doe Votre boucle while (! Countdown.isDone()) {} 'est une très mauvaise idée. – Kayaman

Répondre

4

ScheduledExecutorService est un bon choix, mais vous l'utilisez incorrectement:

Tout d'abord, vous n'avez pas besoin de créer un Thread juste pour planifier une tâche à partir de celui-ci. Il n'ajoute rien à la fonctionnalité, ne gaspille que des ressources. Deuxièmement, après avoir appelé shutdown(), votre scheduler n'acceptera plus de tâches, ce qui est incorrect si vous devez générer plusieurs rapports. Troisièmement, puisque votre code ne fait rien après la fin de la tâche, vous n'avez pas du tout besoin d'appeler get().

Ainsi, le seul code dont vous avez besoin est:

scheduler.schedule(() -> { 
    try { 
     new GetReportList().run(requestId); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
}, 5, TimeUnit.MINUTES); 

Il programmera une tâche et libérer votre fil immédiatement. La tâche sera exécutée cinq minutes plus tard dans un thread distinct géré par scheduler. Si vous avez besoin de contrôle sur les tâches planifiées (vérifiez leurs états, annulez-les etc.), vous pouvez obtenir Future de schedule() et l'enregistrer quelque part, mais selon le code de votre question, vous n'en avez pas besoin .

+0

Parfait, merci beaucoup. C'était exactement ce dont j'avais besoin! Le printemps n'a pas commencé jusqu'à ce que 'get()' soit appelé, mais votre explication a beaucoup plus de sens ... –

+0

@ J.Doe, pourriez-vous, s'il vous plaît, amplifier un peu sur "Le printemps n'a pas commencé jusqu'à get() a été appelé"? Je ne pense pas que ce code puisse affecter le printemps. Peut-être que c'était quelque chose d'autre qui l'a empêché de démarrer? Lorsque vous appelez 'get()' le thread bloque et attend la fin de la tâche (ce qui prend au moins cinq minutes). Je ne pense pas que vous en ayez besoin dans votre programme (du moins cela ne découle pas de votre description) parce que votre 'GetReportList.run()' ne renvoie rien. –

+0

Oui, exactement comme vous l'avez dit. Je voulais dire avant que j'ai fait les changements que vous avez proposés. Maintenant cela fonctionne parfaitement bien - mais ce n'est pas le cas quand j'ai eu 'get()' dans la fonction (sans créer un nouveau thread) car il l'a bloqué. Votre réponse fonctionne parfaitement! –

0

Si vous souhaitez être plus « correct » sur le code, vous pouvez séparer le RequestReport dans sa propre classe qui implémente l'interface Runnable, et transmettre une instance de ladite classe au constructeur du thread

+2

Une classe devrait seulement 'implémenter Runnable' s'il y a une explication évidente et non ambiguë de ce que signifie' run() 'une instance de la classe. Qu'est-ce que cela signifie d'exécuter un RequestReport? Est-ce que cela signifie _generate_ le rapport? Cela signifie-t-il _print_ le rapport? Générer _et_ imprimer le rapport? Quelque chose d'autre? S'il n'y a pas de signification unique, évidente, alors IMO, il est préférable d'implémenter la méthode 'run()' avec une expression lambda ou, avec une classe interne anonyme comme dans l'exemple de l'OP. –