Jboss Java EE contenedor y un ExecutorService
multithreading concurrency (5)
Tengo una aplicación java independiente que utiliza el Servicio de Ejecución para procesar varios trabajos en paralelo
ExecutorService es = Executors.newFixedThreadPool(10);
Ahora quiero reutilizar la misma solución dentro de un bean EJB, pero no estoy seguro de cómo inicializar correctamente ThreadPool, ya que normalmente dejaría el contenedor de Java EE para controlar todos los recursos de subprocesos. ¿Puedo usar el mismo código o hay una forma alternativa correcta de obtener un grupo de subprocesos administrados por Jboss?
Antes de EE7, es posible que desee utilizar WorkManager desde JSR 237
http://docs.oracle.com/javaee/1.4/api/javax/resource/spi/work/WorkManager.html
Esta especificación está actualmente retirada, aún así algunos servidores de aplicaciones la implementan. Yo uso la implementación de IBM en WebSphere 8.5 - IBM WorkManager . Es un recurso totalmente gestionado, disponible en la consola de administración. Tenga en cuenta que no es compatible con la interfaz de Oracle.
Aquí hay un ejemplo para la versión de 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();
}
}
También estoy al tanto de Commonj Workmanager .
Bueno ... la solución de David no me funcionó por las siguientes razones:
- El compilador se quejaba de que java.util.concurrent no está permitido ... lo que tiene sentido en el ámbito de JBOSS.
- También: ¿clase ESTÁTICA pública ...? Lee esto: ¿Por qué no puedes declarar una clase como estática en Java?
Esto es lo que hice:
Mi instalación:
- JBOSS AS 7.1.1
- Java 1.6
- RHEL
- Ejecutando el ejemplo con Gradle y Arquillian :
@Stateless
public class ExecutorBean {
@Asynchronous
public void execute(Runnable command) {
command.run();
}
}
Entonces su cliente se ve así:
@EJB ExecutorBean eb;
@Test
public void testExecutorBean() {
eb.execute(new YourCustomizedRunnableWhichDoesALotOfUsefulStuff());
assertFalse(!true);
}
Sin embargo, tenga cuidado: en mi standalone.xml (o, en general, mi archivo de configuración para JBOSS tengo una sección ''agrupaciones de subprocesos''. Mírelo (si es que usa JBOSSAS) y pruebe los valores allí. Descubra cómo se comporta. Cuando uso hilos con pruebas de arquillian, consigo hilos que se eliminan aunque mi tiempo de actividad es muy alto. Creo que esto tiene que ver con la forma en que se utilizan los microdeploys de Arquillian. Cuando Arquillian termina, todos los hilos no terminados se eliminan. mientras se ejecutan las pruebas ... al menos eso es lo que creo que observo. Por otra parte, todos los hilos terminados se comportaron bien en el sentido de que completaron sus tareas / operaciones.
Espero que esta publicación ayude!
La forma correcta de hacer esto en su EJB es usar ManagedExecutorService, que forma parte de la API de Utilidades de Concurrencia (Java EE7). No debe usar ningún ExecutorService que sea parte de java.util.concurrent en su código de empresa.
Al usar ManagedExecutorService, el contenedor creará y administrará su nuevo hilo.
El siguiente ejemplo es tomado de mi sitio here .
Para crear un nuevo hilo usando un ManagedExecutorService, primero cree un objeto de tarea que implementa Callable. Dentro del método call () definiremos el trabajo que queremos realizar en un hilo separado.
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();
}
}
Entonces necesitamos invocar la tarea pasándola al método submit () del ManagedExecutorService.
@Stateless
public class ReportBean {
@Resource
private ManagedExecutorService executorService;
public void runReports() {
ReportTask reportTask = new ReportTask();
Future<Report> future = executorService.submit(reportTask);
}
}
Si está utilizando JBoss, puede usar org.jboss.seam.async.ThreadPoolDispatcher.
ThreadPoolDispatcher está completamente gestionado.
Para otras clases administradas útiles, vea el paquete: org.jboss.seam.async.
Advertencia obligatoria: no se recomienda crear sus propios subprocesos en un servidor de aplicaciones Java EE (incluso Tomcat), ya que puede ser un gran problema de rendimiento y, en la mayoría de los casos, evitará que funcione la funcionalidad del contenedor, como JNDI. Los nuevos subprocesos no sabrán a qué aplicación pertenecen, el cargador de clases de contexto Thread no se configurará y muchos otros problemas ocultos.
Afortunadamente, hay una manera de hacer que el servidor Java EE administre el grupo de subprocesos a través de Java EE 6 @Asynchronous
y este inteligente patrón de diseño. Portátil a cualquier servidor certificado Java EE 6.
Crea este EJB en tu aplicación.
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();
}
}
Luego puede referirse a este bean en otra parte de su aplicación a través de la inyección de dependencia simple (si el componente que se refiere es un Servlet, Listener, Filter, otro EJB, JSF Managed Bean).
@EJB
private Executor executor;
A continuación, utilice el Executor
como de costumbre.
Si el componente no es otro componente de Java EE, puede buscar el bean a través de:
InitialContext initialContext = new InitialContext();
Executor executor = (Executor) initialContext.lookup("java:module/Executor");