J'ai une application Java autonome qui utilisait ExecutorService pour traiter plusieurs tâches en parallèle.
ExecutorService es = Executors.newFixedThreadPool(10);
Je souhaite maintenant réutiliser la même solution dans un bean EJB, mais je ne sais pas comment initialiser correctement le ThreadPool, car je laisserais normalement le conteneur Java EE pour contrôler toutes les ressources de thread. Puis-je simplement utiliser le même code ou existe-t-il un autre moyen correct d'obtenir un pool de threads gérés par Jboss?
La bonne façon de procéder dans votre EJB consiste à utiliser ManagedExecutorService, composant de l'API Concurrency Utils (Java EE7). Vous ne devez pas utiliser ExecutorService faisant partie de Java.util.concurrent dans votre code d'entreprise.
En utilisant ManagedExecutorService, votre nouveau thread sera créé et géré par le conteneur.
L'exemple suivant est tiré de mon site ici .
Pour créer un nouveau thread à l'aide d'un ManagedExecutorService, créez d'abord un objet de tâche qui implémente Callable. Dans la méthode call (), nous définirons le travail que nous souhaitons effectuer dans un thread séparé.
public class ReportTask implements Callable<Report> {
Logger logger = Logger.getLogger(getClass().getSimpleName());
public Report call() {
try {
Thread.sleep(3000);
catch (InterruptedException e) {
logger.log(Level.SEVERE, "Thread interrupted", e);
}
return new Report();
}
}
Ensuite, nous devons appeler la tâche en la passant à la méthode submit () de ManagedExecutorService.
@Stateless
public class ReportBean {
@Resource
private ManagedExecutorService executorService;
public void runReports() {
ReportTask reportTask = new ReportTask();
Future<Report> future = executorService.submit(reportTask);
}
}
Avertissement obligatoire: il est déconseillé de créer vos propres threads sur un serveur d'applications Java EE (même Tomcat), car cela peut poser un problème de performances considérable et, dans la plupart des cas, empêcher la fonctionnalité de conteneur, telle que JNDI Les nouveaux threads ne sauront pas à quelle application ils appartiennent, le chargeur de classe de contexte Thread ne sera pas défini et de nombreux autres problèmes cachés.
Heureusement, il existe un moyen de faire gérer le pool de threads via le serveur Java EE 6 @Asynchronous
et ce modèle de conception intelligent. Portable sur tout serveur certifié Java EE 6.
Créez cet EJB dans votre application.
package org.superbiz;
import javax.ejb.Asynchronous;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import Java.util.concurrent.Callable;
import Java.util.concurrent.Executor;
@Stateless(name="Executor")
public class ExecutorBean implements Executor {
@Asynchronous
@Override
public void execute(Runnable command) {
command.run();
}
}
Ensuite, vous pouvez faire référence à ce bean ailleurs dans votre application via une injection de dépendance simple (si le composant référent est un servlet, un écouteur, un filtre, un autre bean EJB, JSF géré).
@EJB
private Executor executor;
Ensuite, utilisez Executor
comme d'habitude.
Si le composant n'est pas un autre composant Java EE, vous pouvez rechercher le bean via:
InitialContext initialContext = new InitialContext();
Executor executor = (Executor) initialContext.lookup("Java:module/Executor");
Eh bien ... La solution de David n'a pas fonctionné pour moi pour les raisons suivantes:
Voici ce que j'ai fait:
Mon installation:
- JBOSS AS 7.1.1
- Java 1.6
- RHEL
- Exécuter l'exemple avec Gradle et Arquillian :
@Stateless
public class ExecutorBean {
@Asynchronous
public void execute(Runnable command) {
command.run();
}
}
Ensuite, votre client ressemble à ceci:
@EJB ExecutorBean eb;
@Test
public void testExecutorBean() {
eb.execute(new YourCustomizedRunnableWhichDoesALotOfUsefulStuff());
assertFalse(!true);
}
Attention cependant: dans mon fichier standalone.xml (ou en général mon fichier de configuration pour JBOSS, j'ai une section 'thread-pools'. Regardez-la (si vous utilisez JBOSSAS) et modifiez les valeurs. Modifiez-les. Comment ça se comporte. Quand j'utilise des threads avec des tests arquillian, je reçois des threads qui sont tués même si mon temps de maintien est très élevé. Je pense que cela a à voir avec la façon dont micros déploie arquillian. pendant que les tests sont en cours d'exécution ... du moins, c'est ce que je pense observer. Par contre, tous les fils finis se sont bien comportés en ce sens qu'ils ont terminé leurs tâches/opérations.
J'espère que ce post aide!
Avant EE7, vous souhaiterez peut-être utiliser WorkManager à partir de JSR 237.
http://docs.Oracle.com/javaee/1.4/api/javax/resource/spi/work/WorkManager.html
Cette spécification est actuellement retirée, mais certains serveurs d'applications l'implémentent. J'utilise l'implémentation ibm dans WebSphere 8.5 - IBM WorkManager . C'est une ressource entièrement gérée, disponible dans la console d'administration. Veuillez noter que l'interface n'est pas compatible avec Oracle.
Voici un exemple pour la version IBM:
@Resource(lookup = "wm/default")
WorkManager workManager;
public void process() {
try {
ArrayList<WorkItem> workItems = new ArrayList<WorkItem>();
for (int i = 0; i < 100; i++) {
// submit 100 jobs
workItems.add(workManager.startWork(new Work() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " Running");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void release() {
System.out.println(Thread.currentThread().getName() + " Released");
}
}));
}
// wait for all jobs to be done.
workManager.join(workItems, WorkManager.JOIN_AND, 100000);
} catch (WorkException e) {
e.printStackTrace();
}
}
Aussi, je suis conscient de Commonj Workmanager .
Si vous utilisez JBoss, vous pouvez utiliser org.jboss.seam.async.ThreadPoolDispatcher.
ThreadPoolDispatcher est complètement géré.
Pour d'autres classes managées utiles, voir le package: org.jboss.seam.async.