2010-04-30 6 views
3

J'ai un système très complexe (plus de 100 threads) qui doivent envoyer des emails sans bloquer. Ma solution au problème était d'implémenter une classe appelée EmailQueueSender qui est démarrée au début de l'exécution et a un ScheduledExecutorService qui regarde une file d'attente interne toutes les 500ms et si size()> 0 il la vide. Pendant ce temps, il existe une méthode statique synchronisée appelée addEmailToQueue(String[]) qui accepte un corps contenant un email, sujet..etc comme tableau. Le système fonctionne, et mes autres threads peuvent continuer après avoir ajouté leur email à la file sans bloquer ou même s'inquiéter si l'email a été envoyé avec succès ... il semble juste être un peu désordonné ... ou hackish ... Chaque programmeur obtient ce sentiment dans leur estomac quand ils savent qu'ils font quelque chose de mal ou il y a une meilleure façon. Cela dit, quelqu'un peut-il me gifler au poignet et suggérer un moyen plus efficace d'accomplir cela?Quel motif de conception à utiliser pour une file d'attente filetée

Merci!

Répondre

3

Si vous utilisez Java 6, vous pouvez utiliser les primitives dans le java.util.concurrent paquet. Avoir un thread séparé qui gère l'envoi réel est tout à fait normal. Au lieu d'interroger une file d'attente, je préfère utiliser un BlockingQueue car vous pouvez utiliser un blocage take() au lieu de occupé-en attente.

Si vous souhaitez savoir si l'e-mail a bien été envoyé, votre méthode d'ajout peut renvoyer un Future afin que vous puissiez transmettre la valeur de retour une fois que vous avez envoyé le message. Au lieu d'avoir un tableau de chaînes, je recommande de créer une classe Java (presque triviale) pour contenir les valeurs. La création d'objets est bon marché ces jours-ci.

+0

Juste ce dont j'avais besoin! Déjà mis en œuvre et fonctionne parfaitement. J'ai également changé l'e-mail à une classe interne plutôt que d'un tableau .... Merci beaucoup :) – Submerged

+0

j.u.c est sorti en java 5 –

5

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ThreadPoolExecutor.html

cette seule classe sera probablement gérer la plupart des choses dont vous avez besoin. juste mettre le code d'envoi dans un exécutable et l'ajouter avec la méthode d'exécution. La méthode getQueue vous permet de récupérer la liste actuelle des éléments en attente afin de pouvoir l'enregistrer lors du redémarrage du service de l'expéditeur sans perdre les emails

0

Il existe peut-être déjà un package de messagerie complète, mais je commencerais probablement avec le support de Spring pour email et job scheduling. Lancez un nouveau travail pour chaque e-mail à envoyer, et laissez le calendrier de l'exécuteur envoyer les tâches et vous soucier du nombre de tâches à effectuer. Aucune file d'attente impliquée. Sous le framework, Spring utilise Java Mail pour la partie email, et vous laisse choisir entre ThreadPoolExecutor (comme mentionné par @Lorenzo) ou Quartz. Quartz est meilleur à mon avis, parce que vous pouvez même le configurer de sorte qu'il déclenche vos travaux à des points fixes dans le temps comme les emplois cron (par exemple à minuit). L'avantage d'utiliser Spring est qu'il simplifie grandement le travail avec ces paquets, de sorte que votre travail est encore plus facile.

0

Il existe de nombreux paquets et outils qui aideront à cela, mais le nom générique pour les cas comme celui-ci, largement étudié en informatique, est producer-consumer problem. Il existe diverses solutions bien connues, qui pourraient être considérées comme des «modèles de conception».

1

Je ne sais pas si cela fonctionnerait pour votre application, mais il semble que ce serait le cas. Un ThreadPoolExecutor (ExecutorService -implementation) peut prendre un argument BlockingQueue et vous pouvez simplement ajouter de nouveaux threads à la file d'attente. Lorsque vous avez terminé, vous n'avez qu'à terminer le ThreadPoolExecutor.

private BlockingQueue<Runnable> queue; 
... 
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, new Long(1000), 
       TimeUnit.MILLISECONDS, this.queue); 

Vous pouvez conserver le nombre de threads ajoutés à la file d'attente. Quand vous pensez que vous avez terminé (la file d'attente est vide, peut-être?) Il suffit de comparer cela à

if (issuedThreads == pool.getCompletedTaskCount()) { 
     pool.shutdown(); 
    } 

Si les deux correspondent, vous avez terminé. Une autre façon de terminer le pool est d'attendre une seconde dans une boucle:

try { 
     while (!this.pool.awaitTermination(1000, TimeUnit.MILLISECONDS)); 
} catch (InterruptedException e) {//log exception...}