Hilos

1. Definir Hilo




En sistemas operativos, un hilo de ejecución o subproceso es una característica que permite a una aplicación realizar varias tareas a la vez (concurrentemente). Los distintos hilos de ejecución comparten una serie de recursos tales como el espacio de memoria, los archivos abiertos, situación de autenticación, etc. Esta técnica permite simplificar el diseño de una aplicación que debe llevar a cabo distintas funciones simultáneamente.






Un hilo es básicamente una tarea que puede ser ejecutada en paralelo con otra tarea.






Los hilos de ejecución que comparten los mismos recursos, sumados a estos recursos, son en conjunto conocidos como un proceso. El hecho de que los hilos de ejecución de un mismo proceso compartan los recursos hace que cualquiera de estos hilos pueda modificar éstos. Cuando un hilo modifica un dato en la memoria, los otros hilos acceden a ese dato modificado inmediatamente.






Lo que es propio de cada hilo es el contador de programa, la pila de ejecución y el estado de la CPU (incluyendo el valor de los registros).






El proceso sigue en ejecución mientras al menos uno de sus hilos de ejecución siga activo. Cuando el proceso finaliza, todos sus hilos de ejecución también han terminado. Asimismo en el momento en el que todos los hilos de ejecución finalizan, el proceso no existe más y todos sus recursos son liberados.






Algunos lenguajes de programación tienen características de diseño expresamente creadas para permitir a los programadores lidiar con hilos de ejecución (como Java o Delphi). Otros (la mayoría) desconocen la existencia de hilos de ejecución y éstos deben ser creados mediante llamadas de biblioteca especiales que dependen del sistema operativo en el que estos lenguajes están siendo utilizados (como es el caso del C y del C++).






Un ejemplo de la utilización de hilos es tener un hilo atento a la interfaz gráfica (iconos, botones, ventanas), mientras otro hilo hace una larga operación internamente. De esta manera el programa responde de manera más ágil a la interacción con el usuario. También pueden ser utilizados por una aplicación servidora para dar servicio a múltiples clientes.






2. Ciclo de Vida de un Hilo:


Ciclo de vida de un hilo






Un hilo tiene un ciclo de vida que va desde su creación hsta su terminación. Durante su ciclo de vida cada uno de los hilos o tareas de una aplicación puede estar en diferentes estados, algunos de los cuales se indican a continuación:


• Nacido: Cuando se acaba de crear un hilo, se dice que está nacido, y continúa en ese estado hasta que se invoca el método start() del hilo. La siguiente sentencia crea un nuevo thread pero no lo arranca, por lo tanto deja el thread en el estado de nacido.


Thread miHilo = new MiClaseThread();






Cuando un thread está en este estado, es sólo un objeto Thread vacío o nulo. No se han asignado recursos del sistema todavía para el thread. Así, cuando un thread está en este estado, lo único que se puede hacer es arrancarlo con start().






• Listo: Cuando se invoca el método start() del hilo, se dice que está en estado listo. El método se arranca con la siguiente instrucción, para el caso del hilo miHilo:


miHilo.start();






• Ejecutable: cuando el método start() se ejecuta, crea los recursos del sistema necesarios para ejecutar el thread, programa el thread para ejecutarse, y llama al método run() del thread que se ejecuta en forma secuencial. En este punto el thread está en el estado ejecutable. Se denomina así puesto que todavía no ha empezado a ejecutarse.






• En ejecución: Un hilo en estado de listo de la más alta prioridad, pasa al estado de ejecución, cuando se le asignan los recursos de un procesador, o sea cuando inicia su ejecución. Aquí el thread está en ejecución.Cada hilo tiene su prioridad, hilos con alta prioridad se ejecutan preferencialmente sobre los hilos de baja prioridad.






• No ejecutable: Un hilo continúa la ejecución de su método run(), hasta que pasa al estado de no ejecutable originado cuando ocurre alguno de los siguientes cuatro eventos:






Se invoca a su método sleep().


Se invoca a su su método suspend().


El thread utiliza su método wait() para esperar una condición variable.


El thread está bloqueado durante una solicitud de entrada/salida.






Por ejemplo, en el siguiente fragmento de codigo se pone a dormir










miHilo durante 10 segundos (10.000 milisegundos):


Thread miHilo = new MiClaseThread();


miHilo.start();


try {


miHilo.sleep(10000);


} catch (InterruptedException e){


}










Durante los 10 segundos que miHilo está dormido, incluso si el proceso se vuelve disponible, miHilo no se ejecuta. Después de 10 segundos, miHilo se convierte en "Ejecutable" de nuevo y, si el procesador está disponible se ejecuta. Para cada entrada en el estado "No Ejecutable", existe una ruta de escape distinta y específica que devuelve el thread al estado "Ejecutable". Por ejemplo, si un thread ha sido puesto a dormir durante un cierto número de milisegundos deben pasar esos milisegundos antes de volverse "Ejecutable" de nuevo.






La siguiente es una secuencia de las acciones a realizar para cada entrada en el estado "No Ejecutable":






Si se ha puesto a dormir un hilo, deben pasar el número de milisegundos especificados en sleep().






Si se ha suspendido un hilo, se debe llamar a su método resume ().






Si un hilo está esperando una condición variable, siempre que el objeto propietario de la variable renuncie mediante notify() o notifyAll().






Si un hilo está bloqueado durante la I/O, cuando se complete la I/O.






• Muerto: Un hilo pasa al estado de muerto cuando se termina su método


run(), o cuando se ha invocado su método stop(). En algún momento el sistema dispondrá entonces del hilo muerto. Un hilo puede morir de dos formas:






Muerte natural: se produce cuando su método run() sale normalmente.


Por ejemplo, el bucle while en este método es un bucle que itera 100 veces y luego sale. Por tanto el hilo morirá naturalmente cuando se llegue al final de la iteración, es decir se termina su método run().






public void run() {


int i = 0;


while (i < 100) {


i++;


System.out.println("i = " + i);


}


}










Por muerte provocada: en cualquier momento llamando a su método stop(). El siguiente código crea y arranca miHilo luego lo pone a dormir durante 10 segundos. Cuando el thread actual se despierta, se lo mata con miHilo.stop(). El método stop () lanza un objeto ThreadDeath hacia al hilo a eliminar. El thread moririá cuando reciba


realmente la excepción ThreadDeath.






Thread miHilo = new MiClaseThread();


miHilo.start();


try {


Thread.currentThread().sleep(10000);


} catch (InterruptedException e){


}miHilo.stop();






El método stop () provoca una terminación súbita del método run() del hilo.


Si el método run() estuviera realizando cálculos sensibles, stop() podría dejar el programa en un estado inconsistente. Normalmente, no se debería llamar al método stop() pero si se debería proporcionar una terminación educada como la selección de una bandera que indique que el método run() debería salir. El método stop() se encuentra depreciado en la versión JDK1.2.1.






• Bloqueado: un hilo se encuentra en el estado bloqueado cuando el hilo realiza una solicitud de entrada/salida. Cuando termina la entrada/salida que estaba esperando, un hilo bloqueado queda en el estado listo.






3.


public MiHilo extends Thread


{


public void run()


{


// Aquí el código


}


};






4. Metodos y constructores de la clase Thread que permite la manipulación de Hilos.










Constructores de Thread






Thread(String), Thread() Crea un nuevo thread con o sin nombre.


Thread(Runnable) Crea un nuevo thread pasándole una referencia


a un objeto que implementa Runnable.


Thread(Runnable, String) Igual que el anterior, pero con un nombre.


Thread(ThreadGroup, String) Análogos a los anteriores, pero creando el thread


Thread(ThreadGroup, Runnable) dentro del ThreadGroup especificado.


Thread(ThreadGroup, Runnable, String)










Métodos de la clase Thread






Thread currentThread() Devuelve una referencia al thread actual.


int activeCount() Devuelve el número de threads activos en el grupo.


String getName(), setName(String) Obtiene o establece el nombre de un thread.


ThreadGroup getThreadGroup() Obtiene el grupo al que pertenece el thread.


boolean isAlive() Indica si el thread está vivo o no.


start(), run() Arranca el thread y ejecuta su principal cometido.


int getPriority(), setPriority(int) Obtiene o establece la prioridad del thread


sleep(long), sleep(long, int) Duerme el thread un tiempo especificado.


yield() Cede el control para que otros threads de la misma


prioridad reciban tiempo de CPU.






5.


El interface Runnable


El interface Runnable solamente declara una función miembro denominada run, que han de definir las clases que implementen este interface.


public interface Runnable {


public abstract void run();


}


En nuestro caso, la clase que describe el applet RelojApplet deriva de Applet por lo que no puede derivar también de Thread (no existe la herencia múltiple en Java), sino que tiene que implementar el interface Runnable y definir la función run.