Subido por Memento Etsy

01 - Hilos en Java

Anuncio
Hilos en Java
Hemos visto ya lo que es un Proceso. Una definición sencilla podría ser la de un programa
en ejecución que tiene instrucciones, unos recursos asociados y un estado. Un Proceso
puede competir por el procesador, puede ser bloqueado, etc.
Un Hilo de ejecución, por otra parte, es la secuencia más pequeña de instrucciones que
el Scheduler puede gestionar de forma independiente. En general, podemos decir que en
un Proceso pueden existir uno o más Hilos. Esto es guay porque como ya sabes, los
Procesos no comparten recursos entre sí, pero los Hilos sí pueden, y a demás es posible
hacer que partes separadas de un Proceso se ejecuten a la vez (concurrentemente).
¿Te acuerdas de los programas en C sobre Procesos? En ellos las variables ‘se clonaban’,
de tal manera que dos Procesos Padre e Hijo tenían cada uno su propia variable. Si querías
comunicar dos procesos tenías que inventarte algo extravagante, como un Pipe. Pues bien,
los Hilos permiten hacer cosas como compartir la memoria dinámica entre ellos, lo que
significa que cada Hilo Padre e Hijo accederían a la misma variable común.
Ciclo de Vida
Los Hilos en Java tienen un Ciclo de Vida, cada uno con un diferente estado.
•
New: Cuando se crea un hilo nuevo. En código, se correspondería a hacer:
•
Start: Un hilo no hace nada hasta que no se le arranca. Entonces pasa a ejecutar
el código que tiene asignado en un método llamado run ().
•
Stop: Este método ‘mata’ a un Hilo. Es más habitual dejar que el Hilo termine lo
que está haciendo por su propia cuenta, pero a veces hay que hacerlo.
1
•
Wait, Suspend y Sleep: Un Hilo puede ser parado cuando se le suspende, se le
duerme, cuando está bloqueado en un proceso de entrada/salida, o cuando el hilo
utiliza su método espera a que se cumpla una determinada condición. Son cuatro
instrucciones diferentes, todas ellas detienen el Hilo, pero cada una tiene una
manera de que vuelva a pasar al estado Ejecutable:
o Si un hilo está dormido, pasado el lapso de tiempo.
o Si un hilo está suspendido, después de una llamada a su método resume ().
o Si un hilo está bloqueado en una entrada/salida, cuando concluya su ejecución.
o Si un hilo está esperando una condición, cada vez que la variable que controla
esa condición varíe debe llamarse al método notify () o notifyAll ().
•
IsAlive: Es un método que permite saber si un hilo está en Ejecución o Parado.
•
Yield: Cuando un Hilo no está haciendo nada importante, podemos hacer que le
sugiera al Scheduler que le ignore en favor de otros Hilos. Para eso es yield (),
pero no nos tienen por qué hacer caso.
Trabajando con Hilos
En Java para trabajar con Hilos lo que hacemos es escribir una Clase que herede de Thread
(o implemente Runnable, da igual, ya lo veremos). Esta nueva Clase tendrá sí o sí un
método run () en el que nosotros pondremos el código que queremos que ejecute el Hilo.
Por tanto, desde cualquier punto del programa podemos hacer simplemente:
Y como por arte de magia, se ejecutará a la vez el código del programa Y el del run ().
Bueno vale, esto es más complicado que escribir una clase y cuatro líneas de código. Los
Hilos plantean una serie de problemas, como puede ser la Sincronización. Esto sucede
por ejemplo cuando queremos evitar que dos Hilos accedan a un mismo recurso; o cuando
un Hilo tiene que esperar a que otro Hilo termine antes de continuar.
A parte, la gestión de un Hilo al que se le duerme, se le despierta, etc. se hace siempre
mediante bloques de try – catch. Lo habitual suele ser que dentro del try se para el Hilo,
y desde otro Hilo se envíe un notify () para despertarlo. Esto genera una Excepción que
hace que se ejecute el bloque catch del hilo despertado.
Aunque andar pendientes todo el rato de los bloques try – catch es un poco jaleo, lo difícil
de programar Hilos en Java en realidad es ‘imaginarse’ lo que quieres hacer y pasarlo a
código fuente. En código en sí es algo bastante sencillo, no tiene mucha dificultad.
2
En este esquema tienes un ejemplo simple (y aproximado) de un programa que trabaja
con Hilos en Java. En Verde puedes ver la ejecución principal del programa, y en Azul la
del Hilo (Thread). Lo que sucede es que, en cuanto llegamos al paso 3, se crea la clase
MiThread. Al decirle start () se ejecutará el método run () de MiThread, pero a la vez el
programa principal seguirá ejecutándose por el paso 5. De esta manera podemos delegar
a un Hilo las tareas repetitivas, por ejemplo:
-
Tenemos una clase de acceso a Base de Datos. Sabemos que la Base de Datos
puede tener hasta 10 conexiones simultáneas, así que las abrimos las 10 de forma
permanente y le encargamos a un Hilo diferente que gestione cada conexión. En
principio, los Hilos están parados, pero si nos llega una Insert le mandamos a un
Hilo ejecutar la sentencia. El programa principal gestiona los Hilos, de forma que
en un momento dado puedes estar atendiendo hasta 10 operaciones SQL a la vez.
-
Tenemos un programa que está controlando por Internet las señales de alarma de
una estación eólica (esto es un caso real). Cada vez que llega una señal hay que
hacer algo con ella, pero también hay que reenviarla a otro equipo, registrar la
alarma en una tabla de BBDD, y escribirla en un fichero de log. Un programa
convencional Java tardaría 1 segundo en hacer todo esto de forma secuencial, algo
inadmisible si te están llagando alarmas todo el rato. ¿Solución? Delegas la tarea
pesada a varios Hilos para que todo suceda a la vez. Ahora cada alarma se procesa
en 0,5 segundos.
-
Google tiene su página del buscador web metido en un servidor, que no deja de
ser un programa normal y corriente. Cada vez que alguien quiere buscar algo en
Google, teclea su dirección en un navegador, pone lo que quiere y le da al botón.
¿Cómo hace el servidor para atender simultáneamente las peticiones de millones
y millones de usuarios? Pues efectivamente, haciendo que su buscador genere un
Hilo que atienda a cada petición de cada usuario.
… bueno, y porque son Google y les sobra la pasta, y si tienen que poner mil
millones de servidores pues los ponen y ya.
3
Descargar