Un, Dos, Threads!

Una Introducción Simple a los Threads y sus Conceptos Básicos

Quizás están esperando leer algo sobre threads al comenzar a revisar este artículo, pero les propongo que recorran esta lectura de una forma diferente, qué les parece si comenzamos hablando sobre las casetas telefónicas. Se preguntarán ¿cuál es la relación de los threads con estas?, descubramos mas adelante la analogía.En la ciudad de México no me ha tocado ver en las esquinas de las calles casetas telefónicas con cabina, pero creo que todos hemos visto una en las películas, como aquellas donde Superman entraba a cambiarse para ponerse su disfraz. Ahora que menciono esto, Superman tendría que asegurarse de que nadie mas utilizará la caseta telefónica donde se cambiaba para que nadie descubriera quién era realmente, y debido a que las casetas telefónicas que ocupaba eran públicas y por lo tanto accesible para cualquier usuario, Superman tendría que tener cuidado.

Sabemos que estas casetas al tratarse de un recurso compartido, su uso está sincronizado para atender a un usuario a la vez, así que por este lado Superman podría estar tranquilo, y los usuarios que desean hacer una llamada también lo estarán ya que nadie mas interrumpirá su conversación mientras usa la caseta telefónica. De lo contrario, ¡imagínense lo que pasaría si el uso de la caseta telefónica no estuviera sincronizado!. Cualquier persona entraría a la caseta mientras alguien más la está utilizando, colgaría su llamada y marcaría el nuevo número para comunicarse con alguien más, además de que la cabina se saturaría después de unos cuantos usuarios, seguro que habría bastantes problemas entre los usuarios además de que todos sabrían quién es Superman y se enterarían de todo lo que dice la persona que esté hablando.

También deberíamos considerar que la caseta telefónica podría sufrir descomposturas o requerir mantenimiento, y por lo tanto, debería un técnico estar al tanto de las reparaciones y mantenimiento de ésta, momento en el cual el siguiente usuario de la caseta deberá esperar fuera de esta si llegara alguno en el momento de una reparación o hacer fila si algún usuario ya se encontrara esperando para entrar (¡incluido Superman!) y con esto permitir el trabajo del técnico quien también tuvo que esperar a que terminara la llamada de algún usuario si es que alguno se encontrara usando la caseta en el momento de su arribo. Una vez que el técnico termine con el mantenimiento y/o reparaciones, dará el aviso a los usuarios para que continúen con el uso de la caseta.

Llegando a este punto, seguro que las personas que planificaron la forma en como funcionarían las casetas telefónicas de uso público consideraron las siguientes situaciones:

• Las casetas telefónicas serán de uso compartido
• Los usuarios podrán llegar de forma concurrente a la caseta, por lo que el uso de la misma deberá sincronizarse
• Un técnico se encargará de forma periódica de visitar la caseta telefónica para realizar el mantenimiento y/o hacer las reparaciones correspondientes si estas fueran necesarias. En este momento, la caseta no podría ser utilizada por ningún otro usuario

Bien, ahora será momento de hacer la analogía de la caseta telefónica con los threads y de plasmarla en código para poder echar a andar nuestro ejemplo de manera práctica. Para esto, primero pensemos en el diseño de nuestra caseta  y las diferentes formas en que podrá ser utilizada por sus usuarios, la primera sería su uso para realizar una llamada y dirigida a cualquier tipo de usuario;  otra sería darle mantenimiento, enfocada a los técnicos encargados de que la caseta funcione correctamente. Es un hecho que solo un usuario a la vez podría estar haciendo uso de la caseta telefónica y también es un hecho que diversos usuarios a la vez podrían necesitar hacer uso de ella, por lo que para resolver este problema necesitaremos sincronizar el uso de la caseta y de esta forma asegurarnos que solo un usuario a la vez la utiliza, por otra parte, el técnico encargado del mantenimiento también deberá ser el único que podría estar dentro de la caseta ya que mientras el mantenimiento se realiza, la caseta estaría temporalmente fuera de servicio y no podría ser usada ni por otro técnico ni por cualquier otro tipo de usuario. Con esto, nuestra clase que representa la caseta telefónica quedaría de la siguiente manera:


Como podemos darnos cuenta, tanto el método usarCaseta() y el método darMantenimiento() se encuentran sincronizados (synchronized), con esto aseguramos que los usuarios serán representados como threads podrán acceder uno a la vez a un método en particular, de esta manera tenemos que solo un thread entrará al método usarCaseta()
y un solo thread entrará al método
darMantenimiento(). Pero lo anterior no resuelve del todo nuestro problema de concurrencia, ya que la implementación que tenemos hasta ahora permite que un usuario normal y un técnico puedan hacer uso de la caseta telefónica al mismo tiempo, por lo que deberemos establecer una comunicación entre los usuarios normales y el técnico, esto lo logramos mediante los métodos wait() y
notifyAll(), el método wait() le dice al thread que se este ejecutando que deberá suspender momentáneamente su ejecución hasta que otro thread le notifique que puede continuar, el método wait() será llamado por el thread que representa a un usuario normal cuando detecte que la caseta telefónica se encuentra en mantenimiento, y el método
notifyAll() será llamado por el thread que representa al técnico, este método lo que hace es avisar a los threads que están en espera que pueden continuar con su ejecución, es decir, le dirá a los usuarios que la caseta telefónica esta lista para ser usada nuevamente.

Una vez lista nuestra caseta telefónica, pasemos a la creación de nuestros usuarios. Como mencionamos antes, tanto la implementación de los usuarios normales como la implementación del técnico, deberán estar basados en threads. Existen dos formas en Java mediante las cuales indicamos que una clase será un thread, esto es extendiendo de la clase Thread o implementando la interfaz Runneable, en ambos casos debemos de implementar el método run() cuyo contenido será la actividad que el thread llevará a cabo. Veamos ambos casos:




En el caso del técnico, la implementación quedará de la siguiente manera:



Finalmente nos queda crear una clase de prueba donde echaremos a volar nuestra pequeña aplicación, esta luce de la siguiente manera:



Aquí tenemos la creación del objeto caseta telefónica que será compartido entre los distintos usuarios. Después tenemos la creación de diversos objetos que representan a los usuarios, lo interesante a mencionar en esta parte es la diferencia cuando creamos un thread cuya clase que representa su función hereda de la clase Thread o implementa la interfaz Runnable, en el primer caso solo es necesario crear el objeto de la clase que hereda de Thread y llamaremos al método start(), el cual iniciará la ejecución del thread (la JVM se encargará de llamar al método run() que implementamos para este Thread), para el segundo caso después de crear el objeto de la clase que implementa Runneable, crearemos una instancia de Thread y le pasaremos a través de su constructor el objeto creado un paso antes, posteriormente llamaremos al método start() que tendrá la misma función que en el primer caso.

Con la ejecución de la clase de prueba, podremos ver algo como esto:


Conclusión
Es claro que lo que vimos fue una introducción al tema de threads y a sus conceptos básicos, estos dan lugar a tantas opciones dentro de la creación de aplicaciones en Java, desde las que solo requieran levantar un thread en paralelo al thread principal para que borre archivos temporales hasta aplicaciones complejas que requieran toda una administración de threads, como una aplicación de scheduling donde se podrían tener diversos threads ejecutándose en paralelo y accediendo a recursos compartidos, donde la implementación de una aplicación de este tipo requerirá de toda la dedicación y atención posibles tanto en su diseño como en su desarrollo.

El desarrollo de aplicaciones basadas en threads es un tema complejo y para el cual existen libros completos tratando el tema, sin embargo, espero que esta pequeña introducción sea un primer buen paso para los que se inician en el tema de threads y sus conceptos básicos.

Acerca del Autor
Erick Frausto es egresado de la carrera de Ing. en Informática en UPIICSA-IPN. Además de desempeñarse como Arquitecto JEE, organiza cursos sobre tecnología Java. Lo puedes contactar al siguiente e-mail: erick_frausto10@yahoo.com.mx. Erick dedica este artículo a su hermosa novia Irma.