Manejo de Prioridades de los hilos

Anuncio
HILOS
Conceptos basicos.
Un hilo -algunas veces llamado contexto de ejecución o proceso ligero- es un
flujo de control secuencial dentro de un programa. Un único hilo es similar a un
programa secuencial; es decir, tiene un comienzo, una secuencia y un final, además en
cualquier momento durante la ejecución existe un sólo punto de ejecución. Sin embargo,
un hilo no es un programa; no puede correr por sí mismo, corre dentro de un programa.
Un hilo por si mismo no nos ofrece nada nuevo. Es la habilidad de ejecutar varios hilos
dentro de un programa lo que ofrece algo nuevo y útil; ya que cada uno de estos hilos
puede ejecutar tareas distintas.
El ambiente de desarrollo de Java soporta programación con múltiples hilos por
medio de bibliotecas, el lenguaje mismo y con la ayuda del sistema de tiempo de
ejecución. A continuación se listan las características más importantes con las que cuenta
Java para soportar el uso de hilos.
El método run
Antes que nada, necesitamos proveer a cada hilo con un método run para indicarle qué
debe hacer. El código de este método implementa el comportamiento en ejecución del
hilo y puede hacer, prácticamente cualquier cosa capaz de ser codificada en Java.
Existen dos técnicas para proveer un método run para un hilo:

Haciendo una subclase de Thread y sobrecargando run

Implementando la interface Runnable. Thread es una clase, pero Runnable
es una interfase.
Por convención para seleccionar cual técnica vamos a usar seguiremos la siguiente regla:
``Si las clases que elaboras deben ser subclases de otras clases (un caso común es Applet),
entonces debe usarse Runnable.''
Una vez que el hilo hace algo, podemos manejar el ciclo de vida de un hilo: como crear y
arrancar un hilo, algunas cosas especiales que podemos hacer mientras se ejecuta y como
detenerlo.
Manejo de Prioridades de los hilos
La gran ventaja que ofrecen los hilos (como se puede inferir de lo anteriormente
expuesto) es que corren de manera concurrente. Conceptualmente, esto es cierto, pero en
la práctica generalmente no es posible. La mayoría de las computadoras tienen un sólo
procesador, por lo tanto los hilos corren uno a la vez de forma tal que proveen la ilusión
de concurrencia (esto es llamado scheduling). El sistema de ejecución de Java soporta un
algoritmo determinístico (para el scheduler) llamado fixed priority scheduling. Este
algoritmo asigna tiempo de ejecución a un hilo basado en su prioridad relativa a los
demás hilos que están listos para ejecutarse.
Cuando se crea un nuevo hilo, hereda su prioridad del hilo que lo crea, ésta puede ser
modificada con el método setPriority. Las prioridades son enteros entre
MIN_PRIORITY y MAX_PRIORITY (constantes definidas en la clase Thread). Entre
más alto el entero, más alta la prioridad. Si dos hilos con la misma prioridad están
esperando que el CPU los ejecute, el scheduler escoge uno utilizando round-robin (i.e.
escoge de forma aleatoria, se supone que round-robin ofrece iguales probabilidades de
ejecución a los hilos en cuestión). El hilo seleccionado para ejecución, corre hasta que
alguna de estas condiciones sea verdadera:



Un hilo con mayor prioridad está listo para ejecución.
El hilo cede su lugar (yields), o su método run termina.
En sistemas que soportan rebanadas de tiempo (time slicing), su tiempo
asignado ha expirado.
En ese momento el segundo hilo es atendido por el CPU y así sucesivamente hasta que el
intérprete termina.
Sincronización.
Cuando tenemos varios hilos, muchas veces deseamos que éstos pueden compartir datos,
por lo tanto, es indispensable saber sincronizar sus actividades; esto también nos
permitirá evitar inanición y abrazos mortales.
En muchas situaciones, hilos que se ejecutan concurrentemente comparten información y
deben considerar el estado de las actividades de los demás hilos.
Los segmentos de código dentro de un programa que accedan el mismo objeto desde
hilos separados (concurrentes) se llaman regiones críticas.
Agrupamientos de hilos
Todo hilo en Java es miembro de un grupo. Un grupo de hilos nos da las
herramientas para meter varios hilos en un sólo objeto y manipularlos (a todos) al mismo
tiempo. La clase ThreadGroup implementa los grupos de hilos en Java. El sistema de
ejecución de Java pone a un hilo en un grupo durante la construcción del hilo. Cuando se
crea un hilo, uno puede escoger el grupo al que pertenecerá o permitir que el sistema
seleccione un grupo razonable por omisión para nuestro nuevo hilo. El hilo así creado es
miembro permanente del grupo al cual se una durante su creación -no puede ser
cambiado.
Si se crea un hilo sin especificar su grupo en el constructor, el sistema de ejecución
automáticamente pone el nuevo hilo en el mismo grupo que el hilo que lo crea. Cuando
una aplicación de Java arranca, el sistema de ejecución de Java crea un ThreadGroup
llamado main. A menos que se especifique lo contrario, todos los nuevos hilos que se
creen durante el desarrollo de la aplicación serán miembros del grupo de hilos main.
La clase Thread provee tres constructores que te permiten asignar a un grupo al hilo que
estás creando. Finalmente la clase ThreadGroup provee un conjunto de métodos que te
permiten obtener información como:
 qué otros hilos pertenecen al mismo grupo,




modificar los hilos por grupo:
suspenderlos,
activarlos,
detenerlos, etc.
todo con una sola invocación a los métodos respectivos.
Creación de un hilo.
Esta es solo una forma de hacerlo heredando de Thread
class MiThread extends Thread {
public void run() {
. . .
}
El ejemplo anterior crea una nueva clase MiThread que extiende la clase Thread y
sobrecarga el método Thread.run() por su propia implementacion.
La otra forma es utilizando la interface Runnable
public class MiThread implements Runnable {
Thread t;
public void run() {
// Ejecución del thread una vez creado
}
}
Arranque de un Thread
Las aplicaciones ejecutan main() tras arrancar. Esta es la razón de que main() sea el
lugar natural para crear y arrancar otros threads. La línea de código:
t1 = new TestTh( "Thread 1",(int)(Math.random()*2000)
);
crea un nuevo thread. Los dos argumentos representan el nombre del thread y el tiempo
que queremos que espere antes de imprimir el mensaje.
Al tener control directo sobre los threads, tenemos que arrancarlos explícitamente. En
nuestro ejemplo con:
t1.start();
start(), en realidad es un método oculto en el thread que llama al método run().
Suspensión de un Thread
Puede resultar útil suspender la ejecución de un thread sin marcar un límite de
tiempo. Por ejemplo, para construir un applet con un thread de animación, querrá permitir
al usuario la opción de detener la animación hasta que quiera continuar. No se trata de
terminar la animación, sino desactivarla. Para este tipo de control de thread se puede
utilizar el método suspend().
t1.suspend();
Este método no detiene la ejecución permanentemente. El thread es suspendido
indefinidamente y para volver a activarlo de nuevo necesitamos realizar una invocación
al método resume():
t1.resume();
Parada de un Thread
El último elemento de control que se necesita sobre threads es el método stop(). Se utiliza
para terminar la ejecución de un thread:
t1.stop();
Esta llamada no destruye el thread, sino que detiene su ejecución. La ejecución no se
puede reanudar ya con t1.start(). Cuando se desasignen las variables que se usan en el
thread, el objeto thread (creado con new) quedará marcado para eliminarlo y el garbage
collector se encargará de liberar la memoria que utilizaba.
En nuestro ejemplo, no necesitamos detener explícitamente el thread. Simplemente se le
deja terminar. Los programas más complejos necesitarán un control sobre cada uno de los
threads que lancen, el método stop() puede utilizarse en esas situaciones.
Si se necesita, se puede comprobar si un thread está vivo o no; considerando vivo un
thread que ha comenzado y no ha sido detenido.
t1.isAlive();
Este método devolverá true en caso de que el thread t1 esté vivo, es decir, ya se haya
llamado a su método run() y no haya sido parado con un stop() ni haya terminado el
método run() en su ejecución.
Ciclo de vida de un hilo
Un hilo puede estar en diferentes estados, por ejemplo, si un hilo esta en el estado
born (fig. 1) permanece ahí hasta que se llama al método start, que pone al hilo en el
estado de ready . El hilo que tenga la prioridad más alta entra al estado de running (es
cuando el hilo comienza ha ejecutarse). El hilo entra al estado dead cuando el método
run termina ya sea por que completo sus actividades o por cualquier otra razón.
Una manera común de que un hilo entre al estado blocked es cuando un hilo
solicita entrada o salida de datos. En este caso el hilo entra al estado ready cuando la
entrada/salida se completa.
Cuando el programa llama al método sleep de un hilo que esta corriendo, el hilo
entra al estado sleeping . Un hilo que se encuentra en este estado se cambia al estado
ready cuando expira el tiempo designado para dormir. Si el programa ejecuta el método
interrupt de un hilo que se encuentra en el estado sleeping el hilo se sale de ese estado y
entra al estado ready.
Un hilo entra al estado dead cuando el método run termina o cuando se lanza una
excepción.
born
start
Termina solicitud
entrada/salida
Notify o
nitifyAll
ready
wait
running
Solicita entrada/salida
sleep
wating
Termina intervalo
de dormir
sleeping
dead
bloked
Descargar