Páginas

viernes, 13 de abril de 2012

Ejemplo del patrón de diseño Command y programación concurrente en Java

Java
En la actualizad los procesadores están aumentando su capacidad de proceso principalmente a base de añadir más núcleos, a medida que la tecnología progrese la programación concurrente tomará más importancia para aumentar el rendimiento y para poder soportar más usuarios en las aplicaciones en el caso de la web. Hay ciertas tareas de las aplicaciones que se prestan a ello y que pueden ejecutarse de manera concurrente, por ejemplo, dos tareas que podrían ser son el envío de correos electrónicos y el precalculado de ciertos datos de una base de datos para mejorar los tiempos de acceso en posteriores consultas.

Dependiendo de la aplicación estas dos tareas probablemente no hace falta que sean inmediatas y es interesante que se produzcan fuera del thread que procesa la petición del usuario, más si se trata de una aplicación web. Hay que tener en cuenta que el enviar un correo electrónico, precalcular datos u otras tareas pueden ser algo que lleve una cantidad de tiempo notable, a partir de unos cientos de milisegundos a más de unos segundos. Si tenemos muchos usuarios en la aplicación y realizamos tareas como estas en el mismo thread de la petición el tiempo de respuesta percibido por el usuario será bajo, el número de usuarios concurrentes posibles será menor y escalar en número de usuarios será más dificil. Vamos a ver como solucionar tareas como estas utilizando la programación concurrente que ofrece Java desde la versión 1.5 y aplicando el patrón de diseño Command, patrón que se presta muy bien a ello.

Java en la versión 1.5 añadió el paquete java.util.concurrent para mejorar el soporte que ofrecía java para la programación concurrente con los Threads. La forma más sencilla de empezar a aprovecharlo es a través de la clase Executors que nos permite obtener referencias a objetos ExecutorService que será el que utilicemos para encolar las tareas. La clase Executors tiene varíos métodos que podemos aprovechar, entre ellos:
newFixedThreadPool nos permite obtener un pool de threads de tamaño fijo al que enviar tareas para ejecutarse. Si se envían más tareas que threads hay disponibles en el pool la tarea se encola esperando a que se libere algún thread del pool. newScheduledThreadPool permite programar la ejecución de las tareas a intervalos regulares, es una versión simple de lo que puede ofrecer Quartz ya que no soporta expresiones cron. Si necesitamos que las tareas se ejecuten de forma serializada y no de forma concurrente pero en otro momento de donde se crean podemos usar el ExecutorService devuelto por newSingleThreadExecutor.

Las tareas que se envían a los ExecutorService son clases que implementan la interfaz Runnable o Callable, esta última tiene la ventaja de que puede devolver un resultado y que puede lanzar una excepción. La interfaz Callable tiene un único método, call, y básicamente es una interfaz que sigue el patrón de diseño Command. El patrón Command encapsula los datos de una operación a realizar y desacopla el que crea el objeto del que realmente lo ejecuta.

Dicho todo esto la idea es crear tantas clases que implementen la interfaz Callable como tareas queramos ejecutar fuera del lugar de donde se crean y de forma concurrente, también necesitaremos un ExecutorService que en los siguientes ejemplos está en la clase CallableServiceImpl.

Esta podría ser la interfaz de un servicio que se encarga de ejecutar las tareas que se le envían a través de los métodos submit:

Esta implementación del servicio se encarga de ejecutar de forma concurrente las tareas según el número de threads de un ExecutorService:

El Callable para las tareas que envían correos electrónicos:

La clase mensaje de utilidad que contiene los datos para poder enviar el correo electrónico en EnviarEmailCallable:

La clase Callable que precalcula una serie de datos:

Y utilizando una factoría podemos evitar tener una dependencia sobre estas clases Callable en el código que las usen:

Finalmente el servicio y la implementación del servicio que hace uso de la factoria CallableFactory y el servicio CallableService:

Referencia:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html
http://en.wikipedia.org/wiki/Command_pattern
Patrones de diseño en la programación orientada a objetos
Ejemplo del patrón de diseño State
Ejemplo del patrón de diseño No Operation