ExecutorService y el patrón Command

ExecutorService y el patrón Command

ExecutorService es el nombre que se le ha dado a la API de concurrencia implantada en el JDK7. Su nombre deriva de la única interfaz que hereda de Executor.

Hemos hablado más en profundidad en este blog sobre el patrón Command y hoy venimos a desenmarañar la relación de las clases de esta API para ver una vez más, como el JDK no es ajeno a la implantación de patrones.

Esta API no solo implementa el patrón Command, también usa el Method Factory para devolvernos una instancia. No reflejaremos esto para no complicar más de lo necesario el diagrama UML.

Comencemos por la clase ThreadPoolExecutor. Por supuesto, simplificaremos su explicación, ya que la clase es un pequeño infierno de código usando clases muy variopintas.

Este ThreadPoolExecutor que tomamos como ejemplo, es una de las posibilidades que tenemos a la hora de instanciar un pool de threads de tipo ExecutorService.

Esta clase contiene un atributo de tipo BlockingQueue, que básicamente permite que un hilo introduzca tareas en la cola y otro las consuma de forma segura.

También contiene una inner-class llamada Worker. Esta clase tiene un constructor parametrizado con un Runnable que se encarga de mantener el control de los hilos que ejecutan las tareas. Se podría interpretar como la clase encargada de ejecutar las tareas de la BlockingQueue.

Cuando llamamos al método execute() de la clase principal (recordemos que ThreadPoolExecutor contiene una clase hija Worker), lo que realmente hacemos es comprobar el número de Workers y el máximo que podemos tener para ejecutar la tarea.

  • Si tenemos menos Workers de los que podríamos tener, añadimos uno para ejecutar la tarea.
  • SI tenemos el máximo numero de Workers ya trabajando, metemos en la blockingQueue esa tarea para ser ejecutada más adelante.
  • SI no podemos crear más Workers ni poner en la cola esa tarea, se crea un nuevo hilo solo para desecharla.

A continuación pongo un diagrama UML muy sencillo en el que se aprecian las principales clases, interfaces y relaciones.

executor service uml

¿Veis el parecido con el patrón Command? El invoker aquí esta representado por la clase ThreadPoolExecutor, la cual a través de sus Workers, va resolviendo tareas que el usuario crea.

Recordemos el propósito fundamental del patrón Command: desacoplar la ejecución de la clase que ejecuta una tarea del usuario que la crea, para ser gestionada por un intermediario.

La API Concurrence efectivamente usa el patrón Command, por supuesto de una forma más refinada (ya que usa mecanismos de control de sincronicidad, entre ostras cosas) pero no deja de cumplir con el principio del patrón.

El patrón Command

El patrón Command

El patrón Command es, en un lenguaje muy poco técnico,  la implementeación de un código que permite la creación de peticiones por parte del usuario, su almacenamiento y posterior ejecución sobre un receptor. Nos permite desacoplar el objeto que dispara una acción del objeto que la realiza.

Podemos en base a la «definición» anterior deducir que existen 4 entidades que interactúan entre sí.

  1. El cliente que realiza la petición.
  2. La propia petición.
  3. Un elemento intermediario entre el cliente y el receptor.
  4. El receptor que se encarga de ejecutar la petición.

El cliente es el sujeto que crea la petición en base a dos cuestiones: qué quiere hacer y sobre quién quiere hacerlo.

La petición es el elemento central del patrón. Contiene el código a ejecutar sobre el receptor y por ende, una referencia a este.

El elemento intermediario es el encargado de almacenar las peticiones y provocar su ejecución cuando el cliente lo necesite, o cuando se considere oportuno por cualquier otro motivo.

El receptor simplemente es el sujeto pasivo de la ejecución del comando disparado por el intermediario.

patron command uml

ODIO profundamente el diagrama UML «oficial» del patrón Command, porque creo que despista más que aclara a la hora de comprender por primera vez este patrón. ¿Como es posible que el intermediario no tenga ningún tipo de relación con el cliente y el receptor si? Es necesario intentar comprender por qué está así expuesto el diagrama UML.

El intermediario o invoker no tiene por que tener una relación directa con el cliente, como se ve en el diagrama. Puede o puede no tener relación, a fin de cuentas el cliente que exponemos aquí sólo tiene la obligación de crear el comando y vincularlo con su receptor, pero no de usar el invocador…¿no? El invocador puede estar manejado por un proceso independiente, otro cliente que puede llamar recursivamente cada cierto tiempo a la ejecución de sus comandos por ejemplo.

Nótese por tanto que el cliente que crea el comando puede no ser necesariamente el cliente que llame a su ejecución, aunque por regla general sea así.

En un diagrama de secuencias se aprecia mejor el funcionamiento del patrón, y cómo el invocador está desacoplado del receptor.

Veamos un ejemplo práctico:

Supongamos que nosotros (los clientes) queremos sacar dinero (petición) de un cajero automático (intermediario) de nuestra cuenta bancaria (receptor).

No sabemos cómo es la lógica interna del banco para darnos dinero, ni cómo funciona la máquina por dentro. Solo sabemos que necesitamos ejecutar la acción de sacar dinero sobre nuestra cuenta, así que le decimos al intermediario lo que queremos, generando un comando a medida.

El cajero recibe el comando, parametrizado con el dinero que queremos sacar y la cuenta sobre la que se ejecuta. En un momento determinado, llama a la ejecución de su método, que lleva implícito el código ejecutable que nosotros no conocemos.

El banco recibe las ordenes, haciendo los cambios oportunos en la cuenta, guardando un registro de la transacción, cobrando comisión si hubiera, etc.

Deshacer el último comando

¿Que pasa si el invocador he ejecutado un comando que no debería y necesitamos devolver el receptor de la acción a su estado previo? Esta es una de las características del patrón Command, que nos permite deshacer los cambios de la ultima llamada realizada.

Si todos los comandos tienen un método execute() que se encarga de realizar una operación, podemos obligar a esos mismos comandos a implementar un método definido en la interfaz que deshaga ese cambio. Por ejemplo si tenemos un comando que se encarga de cambiar el volumen de un altavoz, necesitariamos guardar el estado del volumen cada vez que llamemos al método execute() por si acto seguido queremos deshacer los cambios.

El problema de la infinidad de clases comando concretas

¿Te has dado cuenta de la cantidad de comandos concretos que necesitaríamos si quisiéramos crear un altavoz con 100 modos distintos de ejecutarse? Que se iluminase, que tuviera un regulador de volumen, de bajos, de agudos… Un comando para cada modo de cada característica de un solo aparato. ¡Acabaríamos teniendo una cantidad enorme de clases sin apenas código! Afortunadamente, a partir del JDK8, podemos usar las expresiones lambda, que nos permiten solucionar este problema definiendo la implementación del método abstracto que hereda una clase anónima de una interfaz funcional sin necesidad de crearla nosotros.

 

La interfaz funcional y las expresiones lambda en Java

Una interfaz funcional es un tipo muy concreto de interfaz en Java.

Están disponibles a partir de Java 8 y se definen como las interfaces con un único método abstracto. A partir de Java 8 es posible crear interfaces con métodos default, o dicho de otra forma, métodos con implementación por defecto, por eso es necesario recordar que una interfaz funcional no es una interfaz con un sólo método, sino con un solo método abstracto.

Veamos un ejemplo de una interfaz funcional y su notación.

Puedes preguntarte qué ventajas puede haber en definir una interfaz como funcional, y la respuesta es que ninguna, pero en Java 8, simultáneamente con el lanzamiento de este nuevo tipo de interfaces, surgieron las expresiones lambda, las cuales sí poseen muchas características interesantes.

Las expresiones Lambda

Una expresión lambda es una forma de hacer referencia a métodos anónimos cuando hacemos referencias a clases anónimas. ¿Cómo te has quedado? Esta definición da lugar a mas preguntas que respuestas. ¿Qué son una clase anónima y un método anónimo?

  • Una clase anónima es una clase sin un tipo concreto especificado, es decir, una clase que en tiempo de ejecución puede ser cualquier cosa que implemente una determinada interfaz.
  • Un método anónimo es el mismo concepto que la clase anónima trasladada a los métodos: el método de una interfaz implementado a un tipo de objeto no especificado. Se llaman anónimas porque no se especifica el nombre del método en la expresión.

¿Cuales son las ventajas de esto?

  1. Si necesitas momentáneamente una instancia de una clase para algo concreto y no necesitas usarla nunca más, puedes evitar crear una clase específica que ejecute el método que quieres lanzar.
  2. Cuando la implementación es corta, el que la implementación de la clase se encuentre directamente en el lugar donde se usa, puede hacer que el código sea más legible y entendible.

En este ejemplo hemos creado una clase anónima que implementa la interfaz Runnable. Nos da igual de qué tipo sea la clase que implemente Runnable, solo queremos una excusa para ejecutar el método que define la interfaz.

Pero da la casualidad de que además Runnable es una interfaz funcional que implementa el método run(). Esto quiere decir que podemos usar una expresión lambda para implementar el código del método abstracto sobre una clase anónima.

La sintaxis de una expresión lambda es

Interfaz nombre_exp_lambda = (parámetro_función) -> {cuerpo_función}

Si seguimos el ejemplo de la Interfaz Runnable vemos que el método run() no tiene parámetros, y el código del anterior snippet puede ser simplificado aún más:

Hemos creado una clase anónima que llama al único método abstracto de la interfaz que implementa. Así de sencillo.

Las expresiones lambda pueden usar variables y parámetros siempre que estos sean finales o efectivamente finales. Los elementos efectivamente finales son aquellos que

Al principio puede parecer un poco engorroso, pero en cuanto te acostumbras, encuentras la elegancia de un código escueto, funcional, fácil de leer y en ocasiones propicio para mejoras en el rendimiento

Con esta breve introducción creo que ya puedes comenzar a leer la documentación de Oracle al respecto, donde se analizan otras particularidades de este tipo de programación.

 

Los hilos en Java I

El concepto de procesos e hilos no es exclusivo de Java. Los sistemas operativos usan hilos para ejecutar procesos, y como mínimo un proceso debe estar siendo ejecutado en un hilo.

Manejar correctamente los hilos en cualquier aplicación informática es la diferencia más crítica a la hora de hablar de rendimiento, y por eso es necesario comprender profundamente estos conceptos como desarrolladores.

Existen fundamentalmente dos tipos de hilos en la máquina virtual de Java: los hilos del sistema y los hilos demonio (daemon). Los demonios son hilos que se ejecutan a lo largo de la vida de una aplicación controlando diversos aspectos de bajo nivel para el buen funcionamiento de los hilos del sistema. En Java, el daemon más conocido es el garbage collector, que se encarga de liberar espacio en la memoria dinámicamente buscando valores que no están siendo apuntados por ninguna variable.

Los hilos además pueden tener prioridades. Por ejemplo en Linux un proceso (que puede estás definido por uno o mas hilos), puede tener una prioridad de -20 a 20 en función de si es más o menos prioritario. En Java se maneja gracias a una propiedad de la clase Thread, que es la clase fundamental para crear nuevos hilos de sistema, en una escala de 1 a 10.

En Java existen dos maneras de crear una tarea:

  • Proveer una clase que implemente Runnable
  • Crear una clase que extienda Thread

Cada una de ellas tiene sus ventajas e inconvenientes, como explico en esta entrada, pero vemos como implementar cada una de ellas.

La interfaz Runnable

La interfaz Runnable es una interfaz funcional, es decir, una interfaz que implementa un solo método abstracto sin parámetros ni variable de retorno. Se usa para definir una tarea que deberá ejecutar un hilo.

La clase Thread

La clase Thread es la representación de un hilo, y se puede construir de dos formas.

  1. Pasándole por parámetro al constructor de la clase un objeto de tipo Runnable.
  2. Crear una clase que extienda Thread y sobrescribiendo el método run() que hereda de la interfaz Runnable.

Para lanzar el hilo, simplemente tenemos que llamar al método start(), el cual llama al método run() sobreescrito por la clase.

Veamos con un ejemplo simple esto:

Vemos que el hilo principal del programa, creado por la llamada al método main() crea dos nuevos hilos paralelos y los lanza. Podríamos representarlo gráficamente de la siguiente forma:

La máquina virtual de Java acaba la ejecución del programa cuando todos los hilos del sistema terminan.

Hay que tener muy en cuenta que la llamada al método run() de una clase que implemente Runnable no crea un nuevo hilo, solo provoca que la tarea sea ejecutada por el hilo que ha llamado al método.

En la siguiente entrada veremos brevemente cuáles son las APIs de concurrencia de Java y sus principales características.

El patrón Singleton

El patrón Singleton

En ocasiones necesitamos un objeto común a toda la aplicación. Un objeto del que solo queramos una instancia. Normalmente este tipo de objetos se relacionan con objetos con una función específica para toda la aplicación, como por ejemplo un pool de conexiones a una base de datos o las factorías concretas de un patrón Factory. Para proporcionar la funcionalidad del patrón Singleton debemos tener en cuenta tres cosas:

  • Proveer una manera de evitar más de una instancia de la clase.
  • Crear un punto de acceso único a la clase.
  • Tener cuidado con la concurrencia de la aplicación y sus diferentes hilos.

Para el primer y segundo punto debemos considerar dos cosas: la primera es que si queremos un objeto que comparta su estado en toda la aplicación, este objeto deberá tener ser por definición, estático, y la segunda es que si queremos una sola instancia de este, debemos proveer algún tipo de mecanismo para averiguar si existe una instancia creada de este objeto. Una forma de conseguir ambas cosas puede ser la siguiente:

Hemos creado un constructor privado y de esta forma hemos forzando la creación de un objeto estático en un método público que chequea si está inicializado. En caso de no estarlo, lo inicializa y en caso de ya estar inicializado lo devuelve, pero nunca lo crea dos veces.

Nunca lo crea dos veces… a no ser que se de la circunstancia de que dos o más hilos independientes llamen al mismo tiempo el método y chequeen paralelamente que no existe el objeto… en cuyo caso tendríamos dos instancias creadas y un gran problema.

¿Cómo lidiar con el problema del multithreading sobre un método? Java provee la palabra reservada synchronized para evitar que dos o mas hilos ejecuten simultáneamente el mismo bloque de código. Si eres de los que crees que la sincronización es un recurso caro de implementar en términos de rendimiento, no te preocupes, porque parece ser que está muy sobrevalorado ese coste1. Sin embargo, si te sigue preocupando mucho el tema del rendimiento, puedes delegar la inicialización de tu objeto en tiempo de carga de la clase (que es por definición thread-safe) de la siguiente forma:

No te olvides de que si tu objeto va a ser escrito y leído varias veces tendrás que completar tu implementación con

Si por ultimo eres un paranoico o paranoica de la posibilidad de dos instancias de tipo tu Singleton en la misma aplicación deberías anota tu clase como final y evitar la clonación de tu objeto sobrescribiendo el método clone() que todos los objetos heredan de Object.

Por curiosidad, el diagrama UML para una clase Singleton es el siguiente:

1-https://www.ibm.com/developerworks/java/library/j-jtp04223/index.html