Conceptos básicos
Un proceso es un programa ejecutándose dentro de su propio espacio de direcciones. Java es un sistema multiproceso, esto significa que soporta varios procesos corriendo a la vez dentro de sus propios espacios de direcciones. Estamos más familiarizados con el término multitarea, el cual describe un escenario muy similar al multiproceso.
Por ejemplo, consideremos la cantidad de aplicaciones que corren a la vez dentro de un mismo entorno gráfico.
Mientras escribo esto, está corriendo Microsoft Word además de Internet Explorer, Windows Explorer, CD Player y el Volumen Control.
Estas aplicaciones son todos procesos ejecutados dentro de Windows. De esta forma, se puede pensar que los procesos son análogos a las aplicaciones o a programas aislados, pero cada proceso tiene asignado espacio propio de ejecución dentro del sistema.
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.
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.
Clases Relacionadas con los Hilos
El lenguaje de programación Java proporciona soporte para hilos a través de una simple interfaz y un conjunto de clases. La interfaz de Java y las clases que incluyen funcionalidades sobrehilos son las siguientes:
• Thread
• Runnable
• ThreadDeath
• ThreadGroup
• Object
Todas estas clases son parte del paquete Java.lang.
Thread
La clase Thread es la clase responsable de producir hilos funcionales para otras clases. Para añadir la funcionalidad de hilo a una clase simplemente se deriva la clase de Thread y se ignora el método run. Es en este método run donde el procesamiento de un hilo toma lugar, y a menudo se refieren a él como el cuerpo del hilo.
La clase Thread también define los métodos start y stop, los cuales te permiten comenzar y parar la ejecución del hilo, además de un gran número de métodos útiles.
Runnable
Java no soporta herencia múltiple de forma directa, es decir, no se puede derivar una clase de varias clases padre. Esto nos plantea la duda sobre cómo podemos añadir la funcionalidad de Hilo a una clase que deriva de otra clase, siendo ésta distinta de Thread.
Para lograr esto se utiliza la interfaz Runnable.
La interfaz Runnable proporciona la capacidad de añadir la funcionalidad de un hilo a una clase simplemente implementando la interfaz, en lugar de derivándola de la clase Thread.
Las clases que implementan la interfaz Runnable proporcionan un método run que es ejecutado por un objeto hilo asociado que es creado aparte. Esta es una herramienta muy útil y a menudo es la única salida que tenemos para incorporar multihilo dentro de las clases.
ThreadDeath
La clase de error ThreadDeath proporciona un mecanismo que permite hacer limpieza después de que un hilo haya sido finalizado de forma asíncrona. Se llama a ThreadDeath una clase error porque deriva de la clase Error, la cual proporciona medios para manejar y notificar errores.
Cuando el método stop de un hilo es invocado, una instancia de ThreadDeath es lanzada por el moribundo hilo como un error. Sólo se debe recoger el objeto ThreadDeath si se necesita para realiza una limpieza específica a la terminación asíncrona, lo cual es una situación bastante inusual. Si se recoge el objeto, debe ser relanzado para que el hilo realmente muera.
ThreadGroup
La clase ThreadGroup se utiliza para manejar un grupo de hilos de modo conjunto. Esto nos proporciona un medio para controlar de modo eficiente la ejecución de una serie de hilos.
Por ejemplo la clase ThreadGroup nos proporciona métodos stop, suspend y resume para controlar la ejecución de todos los hilos pertenecientes al grupo. Los grupos de hilos también pueden contener otros grupos de hilos permitiendo una jerarquía anidada de hilos. Los hilos individuales tienen acceso al grupo pero no al padre del grupo.
Object
Aunque, estrictamente hablando, no es una clase de apoyo a los hilos, la clase objeto proporciona unos cuantos métodos cruciales dentro de la arquitectura multihilo de Java. Estos métodos son wait, notify y notifyAll. El método wait hace que el hilo de ejecución espere en estado dormido hasta que se le notifique que continúe. Del mismo modo, el método notify informa a un hilo en espera de que continúe con su ejecución. El método notifyAll es similar a notify excepto que se aplica a todos los hilos en espera. Estos tres métodos solo pueden ser llamados desde un método o bloque sincronizado (o bloque de sincronización).
Normalmente estos métodos se utilizan cuando hay ejecución multihilo, es decir, cuando un método espera a que otro método termine de hacer algo antes de poder continuar. El primer hilo espera hasta que otro hilo le notifique que puede continuar. La clase objeto está en la parte superior de la jerarquía de Java, lo cual significa que es el padre de todas las clases. En otras palabras, cada clase Java hereda la funcionalidad proporcionada por la clase objeto, incluyendo los métodos wait, notify y notifyAll.
Estado y Control de Hilos
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 está 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 está 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 está 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
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.
Planificación y Prioridad de Hilos
Java tiene un Planificador (Scheduler), una lista de procesos, que muestra por pantalla todos los hilos que se están ejecutando en todos los programas y decide cuáles deben ejecutarse y cuáles deben encontrarse preparados para su ejecución. Hay dos características de los hilos que el planificador tiene en cuenta en este proceso de decisión.
• La prioridad del hilo (la más importante).
• El indicador de demonio (que pasaremos a explicar en los siguientes apartados).
La regla básica del planificador es que si solamente hay hilos demonio ejecutándose, la Máquina Virtual Java (JVM) concluirá. Los nuevos hilos heredan la prioridad y el indicador de demonio de los hilos que los han creado. El planificador determina qué hilos deberán ejecutarse comprobando la prioridad de todos los hilos. Aquellos con prioridad más alta dispondrán del procesador antes de los que tienen prioridad más baja.
El planificador puede seguir dos patrones, preventivo y no preventivo. Los planificadores preventivos proporcionan un segmento de tiempo a todos los hilos que están corriendo en el sistema. El planificador decide cuál será el siguiente hilo a ejecutarse y llama a resume para darle vida durante un período fijo de tiempo. Cuando finaliza ese período de tiempo, se llama a su método suspend y el siguiente hilo en la lista de procesos será relanzado mediante su método resume.
Los planificadores no preventivos, en cambio, deciden qué hilo debe correr y lo ejecutan hasta que concluye. El hilo tiene control total sobre el sistema mientras esté en ejecución. El método yield es un mecanismo que permite a un hilo forzar al planificador para que comience la ejecución de otro hilo que esté esperando. Dependiendo del sistema en que esté corriendo Java, el planificador será preventivo o no preventivo.
Ejemplo de un hilo que se activa después de 10 segundos
package hilos;
/**
* @author Enrique
*/
import java.util.*;
public class AlarmaRecordatoria {
Timer timer;
public AlarmaRecordatoria(int segundos) {
timer = new Timer();
timer.schedule(new Recordatorio(), segundos * 1000);
}
class Recordatorio extends TimerTask {
public void run() {
System.out.println("Tiempo terminado fin de tarea";
timer.cancel();
}
}
public static void main(String args[]) {
new AlarmaRecordatoria(10);
System.out.println("Tarea programada inicia ";
}
}
Un proceso es un programa ejecutándose dentro de su propio espacio de direcciones. Java es un sistema multiproceso, esto significa que soporta varios procesos corriendo a la vez dentro de sus propios espacios de direcciones. Estamos más familiarizados con el término multitarea, el cual describe un escenario muy similar al multiproceso.
Por ejemplo, consideremos la cantidad de aplicaciones que corren a la vez dentro de un mismo entorno gráfico.
Mientras escribo esto, está corriendo Microsoft Word además de Internet Explorer, Windows Explorer, CD Player y el Volumen Control.
Estas aplicaciones son todos procesos ejecutados dentro de Windows. De esta forma, se puede pensar que los procesos son análogos a las aplicaciones o a programas aislados, pero cada proceso tiene asignado espacio propio de ejecución dentro del sistema.
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.
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.
Clases Relacionadas con los Hilos
El lenguaje de programación Java proporciona soporte para hilos a través de una simple interfaz y un conjunto de clases. La interfaz de Java y las clases que incluyen funcionalidades sobrehilos son las siguientes:
• Thread
• Runnable
• ThreadDeath
• ThreadGroup
• Object
Todas estas clases son parte del paquete Java.lang.
Thread
La clase Thread es la clase responsable de producir hilos funcionales para otras clases. Para añadir la funcionalidad de hilo a una clase simplemente se deriva la clase de Thread y se ignora el método run. Es en este método run donde el procesamiento de un hilo toma lugar, y a menudo se refieren a él como el cuerpo del hilo.
La clase Thread también define los métodos start y stop, los cuales te permiten comenzar y parar la ejecución del hilo, además de un gran número de métodos útiles.
Runnable
Java no soporta herencia múltiple de forma directa, es decir, no se puede derivar una clase de varias clases padre. Esto nos plantea la duda sobre cómo podemos añadir la funcionalidad de Hilo a una clase que deriva de otra clase, siendo ésta distinta de Thread.
Para lograr esto se utiliza la interfaz Runnable.
La interfaz Runnable proporciona la capacidad de añadir la funcionalidad de un hilo a una clase simplemente implementando la interfaz, en lugar de derivándola de la clase Thread.
Las clases que implementan la interfaz Runnable proporcionan un método run que es ejecutado por un objeto hilo asociado que es creado aparte. Esta es una herramienta muy útil y a menudo es la única salida que tenemos para incorporar multihilo dentro de las clases.
ThreadDeath
La clase de error ThreadDeath proporciona un mecanismo que permite hacer limpieza después de que un hilo haya sido finalizado de forma asíncrona. Se llama a ThreadDeath una clase error porque deriva de la clase Error, la cual proporciona medios para manejar y notificar errores.
Cuando el método stop de un hilo es invocado, una instancia de ThreadDeath es lanzada por el moribundo hilo como un error. Sólo se debe recoger el objeto ThreadDeath si se necesita para realiza una limpieza específica a la terminación asíncrona, lo cual es una situación bastante inusual. Si se recoge el objeto, debe ser relanzado para que el hilo realmente muera.
ThreadGroup
La clase ThreadGroup se utiliza para manejar un grupo de hilos de modo conjunto. Esto nos proporciona un medio para controlar de modo eficiente la ejecución de una serie de hilos.
Por ejemplo la clase ThreadGroup nos proporciona métodos stop, suspend y resume para controlar la ejecución de todos los hilos pertenecientes al grupo. Los grupos de hilos también pueden contener otros grupos de hilos permitiendo una jerarquía anidada de hilos. Los hilos individuales tienen acceso al grupo pero no al padre del grupo.
Object
Aunque, estrictamente hablando, no es una clase de apoyo a los hilos, la clase objeto proporciona unos cuantos métodos cruciales dentro de la arquitectura multihilo de Java. Estos métodos son wait, notify y notifyAll. El método wait hace que el hilo de ejecución espere en estado dormido hasta que se le notifique que continúe. Del mismo modo, el método notify informa a un hilo en espera de que continúe con su ejecución. El método notifyAll es similar a notify excepto que se aplica a todos los hilos en espera. Estos tres métodos solo pueden ser llamados desde un método o bloque sincronizado (o bloque de sincronización).
Normalmente estos métodos se utilizan cuando hay ejecución multihilo, es decir, cuando un método espera a que otro método termine de hacer algo antes de poder continuar. El primer hilo espera hasta que otro hilo le notifique que puede continuar. La clase objeto está en la parte superior de la jerarquía de Java, lo cual significa que es el padre de todas las clases. En otras palabras, cada clase Java hereda la funcionalidad proporcionada por la clase objeto, incluyendo los métodos wait, notify y notifyAll.
Estado y Control de Hilos
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 está 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 está 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 está 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
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.
Planificación y Prioridad de Hilos
Java tiene un Planificador (Scheduler), una lista de procesos, que muestra por pantalla todos los hilos que se están ejecutando en todos los programas y decide cuáles deben ejecutarse y cuáles deben encontrarse preparados para su ejecución. Hay dos características de los hilos que el planificador tiene en cuenta en este proceso de decisión.
• La prioridad del hilo (la más importante).
• El indicador de demonio (que pasaremos a explicar en los siguientes apartados).
La regla básica del planificador es que si solamente hay hilos demonio ejecutándose, la Máquina Virtual Java (JVM) concluirá. Los nuevos hilos heredan la prioridad y el indicador de demonio de los hilos que los han creado. El planificador determina qué hilos deberán ejecutarse comprobando la prioridad de todos los hilos. Aquellos con prioridad más alta dispondrán del procesador antes de los que tienen prioridad más baja.
El planificador puede seguir dos patrones, preventivo y no preventivo. Los planificadores preventivos proporcionan un segmento de tiempo a todos los hilos que están corriendo en el sistema. El planificador decide cuál será el siguiente hilo a ejecutarse y llama a resume para darle vida durante un período fijo de tiempo. Cuando finaliza ese período de tiempo, se llama a su método suspend y el siguiente hilo en la lista de procesos será relanzado mediante su método resume.
Los planificadores no preventivos, en cambio, deciden qué hilo debe correr y lo ejecutan hasta que concluye. El hilo tiene control total sobre el sistema mientras esté en ejecución. El método yield es un mecanismo que permite a un hilo forzar al planificador para que comience la ejecución de otro hilo que esté esperando. Dependiendo del sistema en que esté corriendo Java, el planificador será preventivo o no preventivo.
Ejemplo de un hilo que se activa después de 10 segundos
package hilos;
/**
* @author Enrique
*/
import java.util.*;
public class AlarmaRecordatoria {
Timer timer;
public AlarmaRecordatoria(int segundos) {
timer = new Timer();
timer.schedule(new Recordatorio(), segundos * 1000);
}
class Recordatorio extends TimerTask {
public void run() {
System.out.println("Tiempo terminado fin de tarea";
timer.cancel();
}
}
public static void main(String args[]) {
new AlarmaRecordatoria(10);
System.out.println("Tarea programada inicia ";
}
}

