NOTA: Artículo escrito por Arun Gupta el 5 de Diciembre de 2012, traducido y adaptado al español por Jaime Carmona Loeches, miembro de la comunidad JavaHispano, el 25 de Marzo de 2013.
Utilidades de concurrencia para Java EE: borrador preliminar (JSR 236).
INTRODUCCIÓN
Las utilidades de concurrencia para Java EE se están desarrollando bajo la JSR 236, y han publicado un borrador preliminar.
Su objetivo es proporcionar capacidad de concurrencia a los componentes de la aplicación Java EE, sin compromenter la integridad del contenedor.
Para ello, se utilizarán patrones simples que serán integrados fácilmente en el servidor de aplicaciones, haciendo fácil su uso y favoreciendo la integración.
Como muchos sabemos, hace tiempo que existen las utilidades de concurrencia de Java Se 6 (interfaces java.util.concurrent, java.lang.Thread y java.util.Timer). Estas utilidades se podían utilizar de manera correcta en aplicaciones Java standalone (que corrían directamente en la máquina virtual).
Sin embargo, se producían problemas de sincronización al integrarlas en componentes que dependendían de un servidor de aplicaciones, como un Servlet o un EJB.
La especificación JSR 236 nace con la idea de integrar la concurrencia desarrollada previamente en la JSR 166 en los servidores de aplicación de Java EE. Los hilos pasan a ser manejados y proporcionados por el propio contenedor.
NO ME CUENTES ROLLOS, Y ENSEÑAME LÍNEAS DE CÓDIGO, CHAVAL
De acuerdo, pasemos de la teoría a la acción. Existen cuatro interfaces de programación principales:
ManagedExecutorService
ManagedScheduledExecutorService
ContextService
ManagedThreadFactory
Empezamos por la primera: ManagedExecutorService
Es una versión manejada de java.util.concurrent.ExecutorService
.
Es el contenedor quien proporciona la implementación....
<resource-env-ref>
<resource-env-ref-name>
concurrent/BatchExecutor
</resource-env-ref-name>
<resource-env-ref-type>
javax.enterprise.concurrent.ManagedExecutorService
</resource-env-ref-type>
<resource-env-ref>
y quien la ofrece vía JNDI...
Desde código, podemos acceder de la siguiente manera:
@Resource(name="concurrent/BatchExecutor")
ManagedExecutorService executor;
(NOTA: es recomendable enlazar las referencias de JNDI en el subcontextojava:comp/env/concurrent)
Las tareas asíncronas que necesitan ser ejecutadas necesitan implementar una de las siguientes interfaces:
o bien java.lang.Runnable
public class MyTask implements Runnable { public void run() { // business logic goes here } }
o bien java.util.concurrent.Callable
public class MyTask2 implements Callable { public Date call() { // business logic goes here } }
La tarea es entonces enviada al ejecutor, usando el método submit, que retorna una instancia Future.
Future permite conocer el resultado de una tarea, y nos permite conocer si la tarea se ha completado o está pendiente de hacerlo. Veamos un ejemplo:
Future future = executor.submit(new MyTask(), String.class); .......
String result = future.get();
La tarea correrá en un thread separado proporcionado y administrado por el contenedor.
Otro ejemplo podría ser el siguiente:
class MyTask implements Callable { . . . }
class MyTask2 implements Callable { . . . }
ArrayList tasks = new ArrayList<();
tasks.add(new MyTask());
tasks.add(new MyTask2());
List> result = executor.invokeAll(tasks);
En este ejemplo, podríamos enviar al servidor la ejecución de varias tareas, no de una sólo....
Siguiente intefaz: ManagedExecutorService.
Dicha interfaz puede ser configurada para definir una gran variedad de propiedades como:
(NOTA: La especificación no obliga a definir ningún atributo de configuración. Los ejemplos mencionados anteriormente son sólo ejemplos y pueden no ser soportados por todos los servidores de aplicación)
Pasamos a la interfaz ManagedScheduledExecutorService
.
Esta interfaz añade retrasos y táreas períodicas de capacidad a la interfaz ManagedExecutorService
.
Las implementaciones de esta interfaz son proporcionadas por el contenedor y accesible usando la referencia JNDI... (como vimos en la primera interfaz, llamada ManagedExecutorService).
Veamos un ejemplo:
<resource-env-ref>
<resource-env-ref-name>
concurrent/BatchExecutor
</resource-env-ref-name>
<resource-env-ref-type>
javax.enterprise.concurrent.ManagedExecutorService
</resource-env-ref-type>
<resource-env-ref>
y disponible de la siguiente manera:
@Resource(name="concurrent/timedExecutor")
ManagedExecutorService executor;
Las táreas son enviadas entonces utilizando los métodos submit, invokeXXX o scheduleXXX.
ScheduledFuture<?> future = executor.schedule(new MyTask(), 5, TimeUnit.SECONDS);
Esta línea creará y ejecutará una acción que se habilitará con 5 segundos de retraso.
Más control es posible utilizando uno de los nuevos métodos añadidos:
MyTaskListener implements ManagedTaskListener {
public void taskStarting(...) { . . . }
public void taskSubmitted(...) { . . . }
public void taskDone(...) { . . . }
public void taskAborted(...) { . . . } }
ScheduledFuture<?> future = executor.schedule(new MyTask(), 5, TimeUnit.SECONDS, new MyTaskListener());
Aquí, ManagedTaskListener
se utiliza para monitorear el estado de una tarea de futuro.
Veamos la siguiente interfaz,
ManagedThreadFactory.
Dicha interfaz proporciona un método para crear threads que permiten la ejecución en un entorno gestionado.
Un ejemplo simple sería el siguiente:
@Resource(name="concurrent/myThreadFactory")
ManagedThreadFactory factory;
. . .
Thread thread = factory.newThread(new Runnable() { . . . });
siendo concurrent/myThreadFactory
un recurso JNDI.
Hay mucho contenido interesante en el Early Draft, puedes descargarlo y leerlo.
La implementación llegará un poco más adelante y será integrada en el servidor de aplicaciones GlassFish4.
Más información sobre la materia en: