1. HILO
Un hilo es una secuencia de código en ejecución dentro del contexto de un proceso. Los hilos no pueden ejecutarse ellos solos; requieren la supervisión de un proceso padre para correr.
Dentro de cada proceso hay varios hilos ejecutándose. Por ejemplo, Word puede tener un hilo en background chequeando automáticamente la gramática de lo que estoy escribiendo, mientras otro hilo puede estar salvando automáticamente los cambios del documento en el que estoy trabajando. Como Word, cada aplicación (proceso) puede correr varios hilos los cuales están realizando diferentes tareas. Esto significa que los hilos están siempre asociados con un proceso en particular.
Los hilos a menudo son conocidos o llamados procesos ligeros. Un hilo, en efecto, es muy similar a un proceso pero con la diferencia de que un hilo siempre corre dentro del contexto de otro programa. Por el contrario, los procesos mantienen su propio espacio de direcciones y entorno de operaciones. Los hilos dependen de un programa padre en lo que se refiere a recursos de ejecución. La siguiente figura muestra le relación entre hilos y procesos.
2. Estados de un Hilo
El comportamiento de un hilo depende del estado en que se encuentre, este estado define su
modo de operación actual, por ejemplo, si esta corriendo o no. A continuación proporcionamos
la relación de estados en los que puede estar un hilo Java.
• New
• Runnable
• Not running
• Dead
New
Un hilo esta en el estado new la primera vez que se crea y hasta que el método start es
llamado. Los hilos en estado new ya han sido inicializados y están listos para empezar a trabajar,
pero aún no han sido notificados para que empiecen a realizar su trabajo.
Runnable
Cuando se llama al método start de un hilo nuevo, el método run es invocado y el hilo entra
en el estado runnable. Este estado podría llamarse “running” porque la ejecución del método
run significa que el hilo esta corriendo. Sin embargo, debemos tener en cuenta la prioridad de
los hilos. Aunque cada hilo está corriendo desde el punto de vista del usuario, en realidad todos
los hilos, excepto el que en estos momentos esta utilizando la CPU, están en el estado runnable
(ejecutables, listos para correr) en cualquier momento dado. Uno puede pensar conceptualmente
en el estado runnable como si fuera el estado “running”, sólo tenemos que recordar que todos
los hilos tienen que compartir los recursos del sistema.
Not running
El estado not running se aplica a todos los hilos que están parados por alguna razón. Cuando un
hilo está en este estado, está listo para ser usado y es capaz de volver al estado runnable en un
momento dado. Los hilos pueden pasar al estado not running a través de varias vías.
A continuación se citan diferentes eventos que pueden hacer que un hilo esté parado de modo
temporal.
• El método suspend ha sido llamado
• El método sleep ha sido llamado
• El método wait ha sido llamado
• El hilo esta bloqueado por I/O
Para cada una de estas acciones que implica que el hilo pase al estado not running hay una
forma para hacer que el hilo vuelva a correr. A continuación presentamos la lista de eventos
correspondientes que pueden hacer que el hilo pase al estado runnable.
• Si un hilo está suspendido, la invocación del método resume
• Si un hilo está durmiendo, pasarán el número de milisegundos que se ha
especificado que debe dormir
• Si un hilo está esperando, la llamada a notify o notifyAll por parte del
objeto por el que espera
• Si un hilo está bloqueado por I/O, la finalización de la operación I/O en cuestión
Dead
Un hilo entra en estado dead cuando ya no es un objeto necesario. Los hilos en estado dead no
pueden ser resucitados y ejecutados de nuevo. Un hilo puede entrar en estado dead a través de
dos vías:
• El método run termina su ejecución.
• El método stop es llamado.
La primera opción es el modo natural de que un hilo muera. Uno puede pensar en la muerte
de un hilo cuando su método run termina la ejecución como una muerte por causas naturales.
En contraste a esto, está la muerte de un hilo “por causa” de su método stop. Una llamada al
método stop mata al hilo de modo asíncrono.
Aunque la segunda opción suene un poco brusca, a menudo es muy útil. Por ejemplo, es
bastante común que los applets maten sus hilos utilizando el método stop cuando el propio
método stop del applet ha sido invocado. La razón de esto es que el método stop del applet
es llamado normalmente como respuesta al hecho de que el usuario ha abandonado la página
web que contenía el applet y no es adecuado dejar hilos de un applet corriendo cuando el applet
no está activo, así que es deseable matar los hilos.
Hay dos modos de conseguir threads en Java. Una es implementando la interface Runnable, la otra es extender la clase Thread.
La implementación de la interface Runnable es la forma habitual de crear threads. Las interfaces proporcionan al programador una forma de agrupar el trabajo de infraestructura de una clase. Se utilizan para diseñar los requerimientos comunes al conjunto de clases a implementar. La interface define el trabajo y la clase, o clases, que implementan la interface realizan ese trabajo. Los diferentes grupos de clases que implementen la interface tendrán que seguir las mismas reglas de funcionamiento.
Hay una cuantas diferencias entre interface y clase. Primero, una interface solamente puede contener métodos abstractos y/o variables estáticas y finales (constantes). Las clases, por otro lado, pueden implementar métodos y contener variables que no sean constantes. Segundo, una interface no puede implementar cualquier método. Una clase que implemente una interface debe implementar todos los métodos definidos en esa interface. Una interface tiene la posibilidad de poder extenderse de otras interfaces y, al contrario que las clases, puede extenderse de múltiples interfaces. Además, una interface no puede ser instanciada con el operador new; por ejemplo, la siguiente sentencia no está permitida:
Runnable a = new Runnable(); // No se permite
El primer método de crear un thread es simplemente extender la clase 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 implementación. El método run() es donde se realizará todo el trabajo de la clase. Extendiendo la clase Thread, se pueden heredar los métodos y variables de la clase padre. En este caso, solamente se puede extender o derivar una vez de la clase padre. Esta limitación de Java puede ser superada a través de la implementación de Runnable:
public class MiThread implements Runnable {
Thread t;
public void run() {
// Ejecución del thread una vez creado
}
}
En este caso necesitamos crear una instancia de Thread antes de que el sistema pueda ejecutar el proceso como un thread. Además, el método abstracto run() está definido en la interface Runnable tiene que ser implementado. La única diferencia entre los dos métodos es que este último es mucho más flexible. En el ejemplo anterior, todavía tenemos oportunidad de extender la clase MiThread, si fuese necesario. La mayoría de las clases creadas que necesiten ejecutarse como un thread , implementarán la interface Runnable, ya que probablemente extenderán alguna de su funcionalidad a otras clases.
No pensar que la interface Runnable está haciendo alguna cosa cuando la tarea se está ejecutando. Solamente contiene métodos abstractos, con lo cual es una clase para dar idea sobre el diseño de la clase Thread. De hecho, si vemos los fuentes de Java, podremos comprobar que solamente contiene un método abstracto:
package java.lang;
public interface Runnable {
public abstract void run() ;
}
Y esto es todo lo que hay sobre la interface Runnable. Como se ve, una interface sólo proporciona un diseño para las clases que vayan a ser implementadas. En el caso de Runnable, fuerza a la definición del método run(), por lo tanto, la mayor parte del trabajo se hace en la clase Thread. Un vistazo un poco más profundo a la definición de la clase Thread nos da idea de lo que realmente está pasando:
public class Thread implements Runnable
{
{
...
public void run() {
if( tarea != null )
tarea.run() ;
}
}
...
}
4. MÉTODOS
public static Thread currentThread ()
Retorna una referencia al hilo que se está ejecutando actualmente.
Retorna una referencia al hilo que se está ejecutando actualmente.
public static void dumpStack ()
Imprime una traza del hilo actual. Usado sólo con propósitos de depuración.
Imprime una traza del hilo actual. Usado sólo con propósitos de depuración.
public String getName ()
Retorna el nombre del hilo.
Retorna el nombre del hilo.
int getPriority ()
Retorna la prioridad del hilo.
Retorna la prioridad del hilo.
public final boolean isAlive ()
Chequea si el hilo está vivo. Un hilo está vivo si ha sido lanzado con start y no ha muerto todavía.
Chequea si el hilo está vivo. Un hilo está vivo si ha sido lanzado con start y no ha muerto todavía.
public final void isDaemon ()
Devuelve verdadero si el hilo es daemon.
Devuelve verdadero si el hilo es daemon.
public foral void join () throws InterruptedException
Espera a que este hilo muera.
Espera a que este hilo muera.
public final void join (long millis) throws InterruptedException
Espera como mucho millis milisegundos para que este hilo muera.
Espera como mucho millis milisegundos para que este hilo muera.
public final void join (long millis, int nonos) throws InterruptedException
Permite afinar con los nanosegundos nanos el tiempo a esperar.
Permite afinar con los nanosegundos nanos el tiempo a esperar.
public void resume ()
Se usa para recomenzar un hilo que esta suspendido.
NOTA: resume=reanudar.
Se usa para recomenzar un hilo que esta suspendido.
NOTA: resume=reanudar.
public void run ()
Si este hilo fue construido usando un objeto que implementaba Runnable, entonces el método run de ese objeto es llamado. En cualquier otro caso este método no hace nada y retorna.
Si este hilo fue construido usando un objeto que implementaba Runnable, entonces el método run de ese objeto es llamado. En cualquier otro caso este método no hace nada y retorna.
public final void setDaemon (boolean on)
Marca este hilo como daemon si el parámetro on es verdadero o como hilo de usuario si es falso. El método debe ser llamado antes de que el hilo sea lanzado.
Marca este hilo como daemon si el parámetro on es verdadero o como hilo de usuario si es falso. El método debe ser llamado antes de que el hilo sea lanzado.
public foral void setName (String name)
Cambia el nombre del hilo por name.
Cambia el nombre del hilo por name.
public final void setPriority (int newPriority)
Asigna la prioridad newPriority a este hilo.
NOTA: acepta valores de 1 a 10.
Asigna la prioridad newPriority a este hilo.
NOTA: acepta valores de 1 a 10.
public static void sleep (long millis) throws InterruptedException
Hace que el hilo que se está ejecutando actualmente cese su ejecución por los milisegundos especificados en millis. Pasa al estado dormido. El hilo no pierde la propiedad de ningún cerrojo que tuviera adquirido con synchronized.
Hace que el hilo que se está ejecutando actualmente cese su ejecución por los milisegundos especificados en millis. Pasa al estado dormido. El hilo no pierde la propiedad de ningún cerrojo que tuviera adquirido con synchronized.
public static void sleep (long millis, int manos) throws InterruptedException
Permite afinar con los nanosegundos nanos el tiempo a estar dormido.
Permite afinar con los nanosegundos nanos el tiempo a estar dormido.
public void start ()
Hace que el hilo comience su ejecución. La MVJ llamará al método run de este hilo. public void stop ()
Hace que el hilo termine su ejecución.
Hace que el hilo comience su ejecución. La MVJ llamará al método run de este hilo. public void stop ()
Hace que el hilo termine su ejecución.
public void suspend ()
Hace que el hilo interrumpa temporalmente su ejecución.
Hace que el hilo interrumpa temporalmente su ejecución.
public String toString ()
Devuelve una representación en forma de cadena de este hilo, incluyendo su nombre, su prioridad y su grupo.
Devuelve una representación en forma de cadena de este hilo, incluyendo su nombre, su prioridad y su grupo.
public static void yield ()
Hace que el hilo que se está ejecutando actualmente pase al estado listo, permitiendo a otro hilo ganar el procesador.
NOTA: yield=ceder el paso.
Hace que el hilo que se está ejecutando actualmente pase al estado listo, permitiendo a otro hilo ganar el procesador.
NOTA: yield=ceder el paso.
5. La interfaz Runnable
class RunPingPONG implements Runnable { private String word; private int delay; RunPingPONG(String whatToSay, int delayTime) { word =whatToSay; delay=delayTime; } public void run() { try { for(;;) { System.out.print(word+" "); Thread.sleep(delay); } } catch(InterruptedException e) { return; } } public static void main(String[] args) { Runnable ping = new RunPingPONG("ping", 40); Runnable PONG = new RunPingPONG("PONG", 50); new Thread(ping).start(); new Thread(PONG).start();}
}Existen cuatro constructores para crear hilos usando la interfaz Runnable. - public Thread(Runnable target)
así lo usamos en el ejemplo arriba, se pasa solamente la implementación de la interfaz Runnable - public Thread(Runnable target, String name)
se pasa adicionalmente un nombre para el hilo - public Thread(ThreadGroup group, Runnable target)
construye un hilo dentro de un grupo de hilos - public Thread(ThreadGroup group, Runnable target, String name)
construye un hilo con nombre dentro de un grupo de hilos
La interfaz Runnable exige solamente el método run(), sin embargo, normalmente se implementan más métodos para crear un servicio completo que este hilo debe cumplir.
Aunque no hemos guardado las referencias de los hilos en unas variables, los hilos no caen en las manos del recolector de memoria: siempre se mantiene una referencia al hilo en su grupo al cual pertenece.
Aunque no hemos guardado las referencias de los hilos en unas variables, los hilos no caen en las manos del recolector de memoria: siempre se mantiene una referencia al hilo en su grupo al cual pertenece.
El método run() es público y en muchos casos, implementando algún tipo de servicio, no se quiere dar permiso a otros ejecutar directamente el método run(). Para evitar eso se puede recurrir a la siguiente construcción:
class Service { private Queue requests = new Queue(); public Service() { Runnable service = new Runnable() { public void run() { for(;;) realService((Job)requests.take()); } }; new Thread(service).start(); } public void AddJob(Job job) { requests.add(job); } private void realService(Job job) {// do the real work
}} Crear el servicio con Service() lanza un nuevo hilo que actua sobre una cola para realizar su trabajo con cada tarea que encuentra ahí. El trabajo por hacer se encuentra en el método privado realService(). Una nueva tarea se puede añadir a la cola con AddJob(...).
Nota: la construcción arriba usa el concepto de clases anónimas de Java, es decir, sabiendo que no se va a usar la clase en otro sitio que no sea que en su punto de construcción, se declara directamente donde se usa.
BIBLIOGRAFIA